This should be really simple but i cannot figure how to add ProtobufHttpMessageConverter for Spring Controllers while keeping default HttpMessageConverters.
I have setup client side (RestTemplate) but for every request i send there is error 415: content not supported.
Every example i have found so far refers to either Spring Boot or XML configuration, however neither of these work for me.
In the
answer about similar issue,
extending the WebMvcConfigurerAdapter apparently removes default handlers.
It is stated to extend WebMvcConfigurationSupport to keep default handlers, but given implementation doesn't work for Spring 4x as method call super.addDefaultHttpMessageConverters(); requires List of converters.
I have tried variantions on theme but neither seems to work:
#EnableWebMvc
#Configuration
#ComponentScan
public class RestServiceConfiguration extends WebMvcConfigurationSupport {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
// getMessageConverters().add(new ProtobufHttpMessageConverter());
// super.configureMessageConverters(getMessageConverters());
super.addDefaultHttpMessageConverters(converters);
}
}
Could somebody help me to add ProtobufHttpMessageConverter while keeping default converters, without xml configuration ?
With your approach you could make it work. However due to the fact that you extended WebMvcConfigurationSupport and used #EnableWebMvc is isn't working. You are basically configuring web support twice now, as #EnableWebMvc is importing WebMvcConfigurationSupport (actually DelegatingWebMvcConfiguration).
To make your current setup work remove the #EnableWebMvc annotation.
#Configuration
#ComponentScan
public class RestServiceConfiguration extends WebMvcConfigurationSupport {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
super.addDefaultHttpMessageConverters(converters);
}
}
However there is a better way, instead of extend WebMvcConfigurationSupport you should extend WebMvcConfigurerAdapter and implement the extendMessageConverters method instead of the configureMessageConverters.
#EnableWebMvc
#Configuration
#ComponentScan
public class RestServiceConfiguration extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
}
}
Note: The extendMessageConverters method has been added in Spring 4.1.3 for earlier versions use the first method!
Related
How to do the same that I pointed using Spring Boot (+Security)? Implementation of WebMvcConfigurer interface resets a lots settings, which were performed by Spring BOOT did automatically. For example, setting in application.properties "spring.mvc.hiddenmethod.filter.enabled=true" stopped to work. Question in that how to setting a binding of multiple ResourceHandler:ResourceLocation pairs without configuring the extra.
I don't know how to make things right.
#Configuration
#EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**")
.addResourceLocations("classpath:/static/assets/css/");
registry.addResourceHandler("/images/**")
.addResourceLocations("classpath:/static/assets/images/");
registry.addResourceHandler("/js/**")
.addResourceLocations("classpath:/static/assets/js/");
registry.addResourceHandler("/person-storage/**")
.addResourceLocations("classpath:/storage/person-images/");
}
}
I tried to use settings in application.properties for spring boot. I tried to create #Bean of "addResourceHandler" to I won't implement interface WebMvcConfigurer fully.
The context
REST API implemented as Spring boot 1.5.3 project without #EnableWebMvc
The objective
For each API call create a UUID string and inject it into controller methods for audit purposes (the UUID is used in response body and for logging). Should be used as follows:
#PostMapping("/reserveCredits")
public ResponseEntity<Result> reserveCredits(String uuid) {
...
... new Result(uuid) ...
According to the documentation this can be achieved like so:
#Configuration
#EnableWebMvc
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyCustomArgumentResolver());
}
}
The problem
My whole project uses only REST controllers. I'm currently not using #EnableWebMvc and I don't want to introduce it now due to possible conflicts with my existing configuration. When I try using ...
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
... in my #Configuration bean I get BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' due to ServletContext is required.
The questions
How does Spring boot register its default argument resolvers without #EnableWebMvc?
Can I add custom argument resolver without #EnableWebMvc?
Is using #EnableWebMvc highly recommendable and I should retrofit it into my code?
Should I go for alternative solution?
The alternatives
Invasive AOP that overrides method parameter value
HandlerInterceptor that adds the uuid to request parameters and also updates response body
The answer based on the comment by #M. Denium:
Yes it's possible without #EnableWebMvc. Configuration via extension of WebMvcConfigurerAdapter does not have risk of regression impact. RequestMappingHandlerAdapter doesn't have to be autowired into the config class.
#Configuration
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyCustomArgumentResolver());
}
}
I was successfully using #EnableSpringDataWebSupport in my Spring Boot app to enable pagination, sorting and stuff. However, at some point, I had to introduce a custom argument resolver and did it with Java config as follows:
#Configuration
#EnableSpringDataWebSupport
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(renamingProcessor());
}
#Bean
protected RenamingProcessor renamingProcessor() {
return new RenamingProcessor(true);
}
}
It made my new argument resolver work, however completely broke paging and other features, that were automatically configured by #EnableSpringDataWebSupport. I've tried switching WebMvcConfigurerAdapter to alternatives like DelegatingWebMvcConfiguration or WebMvcConfigurationSupport, but no luck -- pagination fails with the exception:
Failed to instantiate [org.springframework.data.domain.Pageable]:
Specified class is an interface
I would appreciate any help or advice how to handle this issue. Similar questions didn't help a lot:
The annotation #EnableSpringDataWebSupport does not work with WebMvcConfigurationSupport?
Failed to instantiate Pageable bean
WebMvcConfigurerAdapter does not work
So, after some investigation, I figured out the solution (perhaps, not ideal one, but still working -- I'd still be happy to see the "right" resolution for the problem from Spring professionals). What I changed is switching from extends WebMvcConfigurerAdapter to extends HateoasAwareSpringDataWebConfiguration (since we're using HATEOAS). I also updated the overridden addArgumentResolvers and now my MvcConfig looks like this:
#Configuration
public class MvcConfig extends HateoasAwareSpringDataWebConfiguration {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
argumentResolvers.add(renamingProcessor());
}
#Bean
protected RenamingProcessor renamingProcessor() {
return new RenamingProcessor(true);
}
}
The issue with Pageable disappeared, and custom RenamingProcessor works like a charm.
Hope this answer will help someone who's facing similar issue.
According to official doc:
Annotation Type Configuration
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions...
#Configuration classes may be composed using the #Import annotation,
not unlike the way that works in Spring XML. Because
#Configuration objects are managed as Spring beans within the
container..
But i can also use #Configuration annotation without #Import. I have tested the code listed below and it works as expected. So what is the purpose to use #Import?
DispatcherServletInitializer
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
WebMvcConfigurerAdapter
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "package.name" })
// #Import(OptionalConfig.class)
public class WebConfig extends WebMvcConfigurerAdapter {
// ...
}
OptionalConfig
#Configuration
public class OptionalConfig {
#Bean(name = "myClass")
public MyClass myClass() {
return new MyClass();
}
}
Service
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyClass myClass; // yes, it works
// ...
}
If component scanning is enabled, you can split bean definitions in multi #Configuration classes without using #Import. And you don't need to provide all of them to the application context constructor.
I think the main purpose for #Import is to provide you a way to simplify multiple configurations registration if you'd like to avoid component scanning (as of Spring Framework 4.2, per reference manual).
There's a note in Spring Reference Documentation about #Import usage:
As of Spring Framework 4.2, #Import also supports references to regular component classes, analogous to the AnnotationConfigApplicationContext.register method. This is particularly useful if you’d like to avoid component scanning, using a few configuration classes as entry points for explicitly defining all your components.
Thus far, we've seen how to break up bean definitions into multiple #Configuration classes and how to reference those beans across #Configuration boundaries. These scenarios have required providing all #Configuration classes to the constructor of a JavaConfigApplicationContext, and this is not always ideal. Often it is preferable to use an aggregation approach, where one #Configuration class logically imports the bean definitions defined by another.
The #Import annotation provides just this kind of support, and it is the direct equivalent of the <import/> element found in Spring beans XML files.
http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
I found a use of using the #Import annotation. I don't think it's the use case.
If you are developing a set of libraries using Spring, probably you don't have a SpringBootApplication. So, you have not enabled any auto-scan to resolve beans.
If a bean declared in a configuration file in the library library-a is referred through dependency injection in library-b you need to use #Import to tell Spring how to resolve the bean.
As we said, in library-a you have this configuration file.
#Configuration
public class ConfigurationA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
In library-b you must have this configuration file if you want to use BeanA
#Configuration
#Import(ConfigurationA.class)
public class ConfigurationB {
#Bean
public BeanB beanB(BeanA beanA) {
return new BeanB(beanA);
}
}
Hope it helps.
With component scanning enabled it's difficult to immediately see where #Import adds value if your view of the world is limited to your own application and its packages. Where it can help is if you are importing bean libraries with their own package structure that you don't want to component scan.
You can place such libraries on your classpath and use #Import to cherry-pick #Configuration classes from within them. That's why it's often referred to as composition because you are composing your #Configuration class from multiple sources.
A typical use case of #Import is when teams develop REST services and realize they need some common configuration.
Every service will have its own namespace i.e. org.mybusiness.specific and will have its #SpringBootApplication (and therefore component scan root) start at this package.
On the other hand, the common library will have its namspace set to org.mybusiness.common and will therefore be out of reach for component scanning.
Thus the need to reference the common configuration class using #Import(CommonConfig.class)
#Import works great when for some reasons you need to register not all but some of the components from a package. In such case #ComponentScan would be too complicated.
See details in the Spring boot component scan include a single class question.
I'm trying to register an instance of HandlerInterceptor in Spring using Java Config without extending WebMvcConfigurationSupport. I'm creating a library with an annotation that, when added to a #Configuration class, registers an interceptor that handles a security annotation.
I had an implementation using WebMvcConfigurationSupport#addInterceptors, but that conflicted with other automatic workings in spring, and overrode some of the application's own logic. It also seems incredibly heavy for something that should be simple. I'm now trying:
#Configuration
public class AnnotationSecurityConfiguration {
#Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping;
#PostConstruct
public void attachInterceptors() {
requestMappingHandlerMapping.setInterceptors(new Object[] {
new SecurityAnnotationHandlerInterceptor()
});
}
}
However, it appears that the interceptor gets registered with a completely different instance of RequestMappingHandlerMapping than the one the application actually uses for web requests. Additionally, when implemeted as a BeanFactoryPostProcessor, I get a NullPointerException in HealthMvcEndpoint when I try beanFactory.getBean(RequestMappingHandlerMapping.class)
Just stating #Blauhirn's comment, WebMvcConfigurerAdapter is deprecated as of version 5.0:
Deprecated as of 5.0 WebMvcConfigurer has default methods (made possible by a Java 8 baseline) and can be implemented directly without the need for this adapter
Refer to the new way to do it:
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyCustomInterceptor())
// Optional
.addPathPatterns("/myendpoint");
}
}
Plus, as stated here, do not annotate this with #EnableWebMvc, if you want to keep Spring Boot auto configuration for MVC.
Edit: This class has since been deprecated. See #bosco answer below for the Spring 5 equivalent.
Figured it out, the solution is to use, simply:
#Configuration
public class AnnotationSecurityConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SecurityAnnotationHandlerInterceptor());
}
}
In spring boot, all beans of type WebMvcConfigurer are automatically detected and can modify the MVC context.