SpringCloud学习6-如何创建一个服务消费者consumer (2)

Component是要把这个Fallback注册到spring容器里,FeignClient在项目启动的时候会读取fallback, 然后从context里读取这个instance,如果没有找到,就启动失败、

见org.springframework.cloud.netflix.feign.HystrixTargeter#getFromContext

private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, Class<?> beanType, Class<T> targetType) { Object fallbackInstance = context.getInstance(feignClientName, beanType); if (fallbackInstance == null) { throw new IllegalStateException(String.format( "No " + fallbackMechanism + " instance of type %s found for feign client %s", beanType, feignClientName)); } if (!targetType.isAssignableFrom(beanType)) { throw new IllegalStateException( String.format( "Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s", beanType, targetType, feignClientName)); } return (T) fallbackInstance; }

@RequestMapping 则是不得已而为之了。前文provider-demo里,我们把api抽取成UserApi

@RequestMapping("/api/v1/users") public interface UserApi { @GetMapping("http://www.likecs.com/") List<UserVo> list(); @GetMapping("/fallback") String fallback(); }

这里的RequestMapping会被spring启动的到时候扫描到,在初始化RequestMappingHandlerMapping的时候,扫描所有的bean,把RequestMapping的bean给注册RequestMapping. 这时候,它不管你是不是controller的。我们FeignClient所声明的接口上有@RequestMapping,也会被扫描。而我们Fallback也继承,也会有@RequestMapping,这时候重复定义RequestMapping会报错

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'com.test.cloud.client.UserClient' method public abstract java.util.List<com.test.cloud.vo.UserVo> com.test.cloud.api.UserApi.list() to {[/api/v1/users/],methods=[GET]}: There is already 'userClientFallback' bean method public java.util.List<com.test.cloud.vo.UserVo> com.test.cloud.client.UserClientFallback.list() mapped.

事实上,我们并不是要将FeignClient给注册到RequestMapping里的,而且OpenFeign也有自己的一套注解方案。只是spring-cloud为了方便集成和简化OpenFeign的用法,把Spring-Web的注解做了适配。不好的地方是RequestMapping的扫描并没有排除。

以下代码会找到方法注解@RequestMapping.

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(java.lang.reflect.AnnotatedElement)

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }

而RequestMapping这个bean创建完后会扫描所有bean, 并注册

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register

public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } } private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) { HandlerMethod handlerMethod = this.mappingLookup.get(mapping); if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) { throw new IllegalStateException( "Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" + handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped."); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwsdw.html