在使用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
註解也可以接收到實體類了