Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
inheritance-aware Spring MethodArgumentResolver; spring-boot based workaround for SPR-11055
import java.lang.reflect.Method;
import java.util.stream.Stream;
import org.apache.commons.lang3.ClassUtils.Interfaces;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
public class InheritanceAwareMethodArgumentResolverWrapper implements HandlerMethodArgumentResolver {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
public static HandlerMethodArgumentResolver wrap(HandlerMethodArgumentResolver wrapped) {
return wrapped instanceof InheritanceAwareMethodArgumentResolverWrapper ? wrapped
: new InheritanceAwareMethodArgumentResolverWrapper(wrapped);
}
private static Stream<MethodParameter> parameterHierarchy(MethodParameter parameter) {
return MethodUtils.getOverrideHierarchy(parameter.getMethod(), Interfaces.INCLUDE).stream()
.map(m -> methodParameter(m, parameter.getParameterIndex()));
}
private static MethodParameter methodParameter(Method m, int index) {
final MethodParameter result = new SynthesizingMethodParameter(m, index);
result.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return result;
}
private final HandlerMethodArgumentResolver wrapped;
public InheritanceAwareMethodArgumentResolverWrapper(HandlerMethodArgumentResolver wrapped) {
super();
this.wrapped = Validate.notNull(wrapped);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameterHierarchy(parameter).anyMatch(wrapped::supportsParameter);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
final MethodParameter mp = parameterHierarchy(parameter).filter(wrapped::supportsParameter).findFirst().orElse(null);
return mp == null ? null : wrapped.resolveArgument(mp, mavContainer, webRequest, binderFactory);
}
}
// then create an autoconfiguration with:
@Bean
public BeanPostProcessor requestHandlerAdapterPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
final RequestMappingHandlerAdapter handlerAdapter = (RequestMappingHandlerAdapter) bean;
handlerAdapter.setArgumentResolvers(wrap(handlerAdapter.getArgumentResolvers()));
handlerAdapter.setCustomArgumentResolvers(wrap(handlerAdapter.getCustomArgumentResolvers()));
}
return bean;
}
private List<HandlerMethodArgumentResolver> wrap(List<HandlerMethodArgumentResolver> resolvers) {
return resolvers == null ? null : resolvers.stream().map(InheritanceAwareMethodArgumentResolverWrapper::wrap).collect(toList());
}
};
}
@mbenson

This comment has been minimized.

Copy link
Owner Author

mbenson commented Feb 25, 2016

Finally, my autoconfiguration is annotated with:

@ConditionalOnClass(WebMvcConfigurer.class)
@cforce

This comment has been minimized.

Copy link

cforce commented Feb 25, 2016

package com.daimler.daivb.core.web.autoconfigure;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

@Configuration
@ConditionalOnClass(WebMvcConfigurer.class)
public class FeignAwareMvcResolverConfiguration {
    // then create an autoconfiguration with:
    @Bean
    public BeanPostProcessor requestHandlerAdapterPostProcessor() {
        return new BeanPostProcessor() {

            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof RequestMappingHandlerAdapter) {
                    final RequestMappingHandlerAdapter handlerAdapter = (RequestMappingHandlerAdapter) bean;
                    handlerAdapter.setArgumentResolvers(wrap(handlerAdapter.getArgumentResolvers()));
                    handlerAdapter.setCustomArgumentResolvers(wrap(handlerAdapter.getCustomArgumentResolvers()));
                }
                return bean;
            }

            private List<HandlerMethodArgumentResolver> wrap(List<HandlerMethodArgumentResolver> resolvers) {
                return resolvers == null ? null
                        : resolvers.stream().map(InheritanceAwareMethodArgumentResolverWrapper::wrap)
                                .collect(Collectors.toList());
            }
        };
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.