EnableSpringDataWebSupport doesn't seem to work well with WebMvcConfigurerAdapter - java

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.

Related

Spring Boot: The bean 'auditLogDao' could not be injected as a 'AuditLogDao' because it is a JDK dynamic proxy

I am getting the following error in a Spring Boot project on which I work:
The bean 'auditLogDao' could not be injected as a '{redactedpathwithcorporatename}.AuditLogDao' because it is a JDK dynamic proxy that implements:
org.springframework.data.jpa.repository.JpaRepository
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on #EnableAsync and/or #EnableCaching.
I have tried a variety of solutions on StackOverflow without success, specifically:
Checking that I am indeed calling the interface, not the implementation.
Adding #Component to the top of SwitchUserFilter
Changing #Resource to #Autowired.
AuditLogDao.java
public interface AuditLogDao extends JpaRepository<AuditLog, String> {}
AuditLogService.java
public interface AuditLogService {
AuditLog save(final AuditLog auditLog);
}
AuditLogServiceImplementation.java
public class AuditLogServiceImplementation implements AuditLogService{
#Resource private AuditLogDao auditLogDao;
#Override
public AuditLog save(AuditLog auditLog) {
return auditLogDao.save(auditLog);
}
}
The file where I actually want to use the service to save information
SwitchuserFilter.java
public class SwitchUserFilter
extends org.springframework.security.web.authentication.switchuser.SwitchUserFilter {
#Resource AuditLogService logService;
'''
logService.save(auditLog);
'''
}
I am relatively new to Spring Boot, so an explanation of why it fixes the problem would be appreciated.
I believe the following code will solve your problem. Add it to the AuditLogServiceImplementation and remove the #Resource annotation from the auditLogDao.
#Autowired
private ListableBeanFactory beanFactory;
#EventListener({ContextRefreshedEvent.class})
void contextRefreshedEvent() {
auditLogDao = beanFactory.getBean(AuditLogDao.class);
}
You can do a similar trick in the filter too, whatever more comfortable for you.
I don't know what is the exact problem, but it's some kind of circular-dependency-like issue.
So by manually importing any bean which is affected in this loop, you can resolve the loop. You will set this one particular dependency AFTER Spring had created all of the other beans.

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

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

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.

XML Configuration Equivalent of #EnableHypermediaSupport

I was reading through an article on REST API development with Spring-HATEOAS, as well as browsing the project page and docs, but have so far only seen example code using Java configuration. Is there an XML equivalent to #EnableHypermediaSupport and the configuration seen in the article's example?
#Configuration
#ComponentScan
#EnableWebMvc
#EnableHypermediaSupport
class WebMvcConfiguration extends WebMvcConfigurationSupport {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer c) {
c.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public MultipartResolver multipartResolver() { .. }
}
There currently is none. Feel free to open a ticket in the issue tracker.

Categories

Resources