Is it possible to configure custom argument resolvers without #EnableWebMvc and WebMvcConfigurerAdapter - java

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());
}
}

Related

Cannot Inject Service in HandlerInterceptorAdapter, Getting NullPointerException

I have a service-client project which is in normal spring application , not spring boot .its used for mainly logging related things.which contains Interceptor , loggingservice impl class and some model classes for logging. I have added this module as a dependency to main application in pom.xml.and i was able to inject and use the loggingService beans within the service layers of the main application.
Am getting NullPointerException while auto-wiring loggingService within the interceptor .The bean is not available within the interceptor.but like i said it can be injected and used within the main application.
Also am not able to read properties using #Value within the interceptor.
This is my Interceptor class .
#Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoggingService loggingService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
loggingService.info("Am in prehandle");
return true;
}
}
This is my configuration class where i register the interceptor with the main application
#Component
public class LoggingConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLoginInterceptor());
}
#Bean
public LoggingInterceptor getLoginInterceptor() {
return new LoggingInterceptor();
}
}
My question is almost similar to this post Cannot Autowire Service in HandlerInterceptorAdapter , but its different like am referring the interceptor from another module , and like they suggested i tried to create the bean from the application.
But the issues am facing right now is
getting NullPointerException while injecting loggingService within interceptor, but its working in main application
#Value annotation also return null, not able to read from properties
You have 2 possible solutions.
Mark your LoggingConfig as #Configuration instead of #Copmponent
Inject the LoggingInterceptor instead of referencing the #Bean method
Option 1: LoggingConfig as #Configuration
Your LoggingConfig is marked as an #Component whereas it should be marked as an #Configuration. The difference is that whilst it is allowed to have an #Bean method on an #Component it operates in a so-called lite mode. Meaning you cannot use method references to get the instance of a bean (this is due to no special proxy being created). This will lead to just a new instance of the LoggingInterceptor being created but it isn't a bean.
So in short what you are doing is equivalent to registry.addInterceptor(new LoggingInterceptor()); which just creates an instance without Spring knowing about it.
When marking the LoggingConfig as an #Configuration a special proxy will be created which will make the LoggingInterceptor a proper singleton bean, due to the method call being intercepted. This will register the bean in Spring and you will be able call the method.
NOTE: You actually endup with 2 instances of the LoggingInterceptor one due to the #Component on it the other through the #Bean. Remove the #Component.
Option 2: Inject the LoggingInterceptor.
As your LoggingInterceptor is marked as an #Component Spring will already create an instance (you actually have 2 instances of it created in your current setup). This instance you can inject into your LoggingConfig.
#Component
public class LoggingConfig implements WebMvcConfigurer {
private LoggingInterceptor loggingInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
With this you can remove the #Bean method as you will get the proper one injected into your LoggingConfig class. The class can also remain an #Component in this case. Although I would recommend using #Configuration as to also properly stereotype it.
NOTE: If you are on a recent Spring version you can use #Configuration(proxyBeanMethods=false). This will make a lite-configuration (just like an #Component) but it is still marked properly as a configuration class.

Why classes extending WebSecurityConfigurerAdapter and not declaring any bean are annotated with configuration?

According to #Configuration documentation:
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
#Configuration
public class AppConfig {
#Bean
public MyBean myBean() {
//instantiate, configure and return bean ...
}
}
As I remember always I came across classes extending WebSecurityConfigurerAdapter which didn't contain any #Bean methods and were annotated with #Configuration.
It is even in official blog and some examples, see:
https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security
#Configuration
#EnableWebSecurity
public class HelloWebSecurityConfiguration
extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
or here: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html
#Order(1) 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") 3
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
}
}
Why this classes are annotated with #Configuration even though there are no #Bean methods?
Beans are imported using the secondary "#Enable" annotation
Spring features such as asynchronous method execution, scheduled task execution, annotation driven transaction management, and even Spring MVC can be enabled and configured from #Configuration classes using their respective "#Enable" annotations. See #EnableAsync, #EnableScheduling, #EnableTransactionManagement, #EnableAspectJAutoProxy, and #EnableWebMvc for details.
from EnableWebSecurity:
Add this annotation to an #Configuration class to have the Spring Security configuration defined in any WebSecurityConfigurer or more likely by extending the WebSecurityConfigurerAdapter base class and overriding individual methods:

What is the use case of #Import annotation?

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.

Spring adding ProtobufHttpMessageConverter to controllers without xml config

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!

Register Spring HandlerInterceptor Without WebMvcConfigurationSupport

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.

Categories

Resources