在使用Feign的时候,一般先写一个接口类,而后再写实现类,根据官网的例子接下来编写一个简单的Feign的请求例子spring
@FeignClient("spring-cloud-eureka") public interface FeignDemoApi { @RequestMapping("/testFeign") public String testSpringMvc(@RequestBody User user); }
而后实现类以下mvc
@RestController public class FeignDemoApiImpl implements FeignDemoApi{ @Override public String testSpringMvc(User user) { return user.getName(); } }
而后测试类编写以下app
@RequestMapping("/testSpringmvc") public void test6(){ User user =new User(); user.setName("test"); user.setAge(18); feignDemoApi.testSpringMvc(user); }
发如今客户端进行接收的时候发现接收到的User
为nullide
而后从网上查资料才知道须要在实现类的入参中也加入@RequestBody
注解,这样才能接收到参数。可是不由有个疑问,咱们查看@RequestMapping
和@RequestBody
这两个注解的代码中都没有@Inherited
这个可支持继承的注解,那么@RequestMapping
为何能发挥做用?而@RequestBody
却不能发挥做用呢?测试
而后通过查资料了解到,SpringMvc在初始化时候会将带有@Controller
初始化进Spring容器中,其实例化的类型为RequestMappingInfo
类,此时在SpringMvc中会加载@Controller
类注解下的全部带有@ RequestMapping
的方法,将其@RequestMapping
中的属性值也加载进来,若是在本类方法上找不到@RequestMapping
注解信息的话,那么就会寻找父类相同方法名的@RequestMapping
的注解。具体在AnnotatedElementUtils
类中的searchWithFindSemantics
方法中有下面的一段。表示在父类中寻找注解。this
// Search on interfaces for (Class<?> ifc : clazz.getInterfaces()) { T result = searchWithFindSemantics(ifc, annotationType, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } }
此时就能知道为何在子类中没有加@RequestMapping
注解,可是却享有父类@RequestMapping
注解的效果了。.net
在上面的注解中咱们了解到虽然@RequestMapping
不支持继承,可是子类享有一样效果的缘由就是在判断的时候若是子类没有就去父类找,可是在测试中咱们发现@RequestBody
是没有享受此效果的,因此我猜想在判断是否有注解的时候只是判断本类有没有此注解而没有判断父类。3d
通过查询资料,发如今SpringMvc中使用RequestResponseBodyMethodProcessor
来进行入参和出参的解析,其中使用supportsParameter
来判断是否有@RequestBody
的注解code
@Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); }
咱们发现事实如咱们猜想的同样。blog
咱们也能够像@RequestMapping
注解同样进行判断若是本类没有的话,那么就对其父类进行判断。建立一个配置类而后将自定义的ArgumentResolvers
放入到RequestMappingHandlerAdapter
中
@Configuration public class MyWebMvcConfigu implements BeanFactoryAware { private ConfigurableBeanFactory beanFactory; @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; //判断其父类是否有注解 public static <A extends Annotation> MethodParameter interfaceMethodParameter(MethodParameter parameter, Class<A> annotationType) { if (!parameter.hasParameterAnnotation(annotationType)) { for (Class<?> itf : parameter.getDeclaringClass().getInterfaces()) { try { Method method = itf.getMethod(parameter.getMethod().getName(), parameter.getMethod().getParameterTypes()); MethodParameter itfParameter = new MethodParameter(method, parameter.getParameterIndex()); if (itfParameter.hasParameterAnnotation(annotationType)) { return itfParameter; } } catch (NoSuchMethodException e) { continue; } } } return parameter; } @PostConstruct public void modifyArgumentResolvers() { List<HandlerMethodArgumentResolver> list = new ArrayList<>(requestMappingHandlerAdapter.getArgumentResolvers()); // RequestBody 支持接口注解 list.add(0, new RequestResponseBodyMethodProcessor(requestMappingHandlerAdapter.getMessageConverters()) { @Override public boolean supportsParameter(MethodParameter parameter) { return super.supportsParameter(interfaceMethodParameter(parameter, RequestBody.class)); } @Override // 支持@Valid验证 protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) { super.validateIfApplicable(binder, interfaceMethodParameter(methodParam, Valid.class)); } }); // 修改ArgumentResolvers, 支持接口注解 requestMappingHandlerAdapter.setArgumentResolvers(list); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (ConfigurableBeanFactory) beanFactory; } }
而后咱们就能够发现再从新调用之后子类没有加入@RequestBody
注解也可以接收到实体类了