feign是springCloud微服务之间远程调用的一个组件。首先从@EnableFeignClients注解看起,@EnableFeignClients注解注入了一个FeignClientsRegistrar类,FeignClientsRegistrar它是一个注册器,看名字就能知道,通过扫描某些特性的类注册成spring的Bean。
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(FeignClientsRegistrar.class)public @interface EnableFeignClients {}
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware, EnvironmentAware {}
FeignClientsRegistrar类实现了ImportBeanDefinitionRegistrar,ResourceLoaderAware,EnvironmentAware这几个接口。
- ImportBeanDefinitionRegistrar:实现这个接口的bean可以手动注册Bean到spring容器中;
- ResourceLoaderAware:支持可以classPath下的配置文件;
- EnvironmentAware:可以读取spring的全局配置信息。
下面看下实现ImportBeanDefinitionRegistrar的registerBeanDefinitions方法
@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 读取自定义的配置文件信息,如果没有定义则读取默认的配置registerDefaultConfiguration(metadata, registry);// 注册FeignClientregisterFeignClients(metadata, registry);}
public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 拿到一个扫描器ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());// 添加一个过滤器,过滤添加了@FeignClient注解的类AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);// 查看EnableFeignClients注解有没有配置clients属性,如果没有配置则扫描加了@FeignClient注解的类final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");if (clients == null || clients.length == 0) {scanner.addIncludeFilter(annotationTypeFilter);basePackages = getBasePackages(metadata);}else {final Set<String> clientClasses = new HashSet<>();basePackages = new HashSet<>();for (Class<?> clazz : clients) {basePackages.add(ClassUtils.getPackageName(clazz));clientClasses.add(clazz.getCanonicalName());}AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {@Overrideprotected boolean match(ClassMetadata metadata) {String cleaned = metadata.getClassName().replaceAll("\\$", ".");return clientClasses.contains(cleaned);}};scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));}for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);// 遍历所有候选的BeanDefinitionfor (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");// 查找FeignClient注解身上设置的属性Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name = getClientName(attributes);// 注册FeignClient的配置文件的信息registerClientConfiguration(registry, name,attributes.get("configuration"));// 注册beanregisterFeignClient(registry, annotationMetadata, attributes);}}}}
private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);validate(attributes);definition.addPropertyValue("url", getUrl(attributes));definition.addPropertyValue("path", getPath(attributes));String name = getName(attributes);definition.addPropertyValue("name", name);definition.addPropertyValue("type", className);definition.addPropertyValue("decode404", attributes.get("decode404"));definition.addPropertyValue("fallback", attributes.get("fallback"));definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);String alias = name + "FeignClient";AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be nullbeanDefinition.setPrimary(primary);String qualifier = getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias = qualifier;}BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}
上面registerFeignClient方法主要是将@FeignClient注解中的参数值构造成一个FeignClientFactoryBean的BeanDefinition,然后客户端注入那个Bean时,比如通过@Autowired注入时会调用FactoryBean的getObject方法拿到Bean对象。
@Overridepublic Object getObject() throws Exception {return getTarget();}
<T> T getTarget() {FeignContext context = applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);// 如果没有传入url,直传了name,将会根据集群名字利用ribbon的负载均衡调用// 如果既传入了url又传入了name,将会之间根据url调用if (!StringUtils.hasText(this.url)) {String url;if (!this.name.startsWith("http")) {url = "http://" + this.name;}else {url = this.name;}url += cleanPath();return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,this.name, url));}if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {this.url = "http://" + this.url;}String url = this.url + cleanPath();Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient)client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);// 会利用jdk动态代理生成代理对象返回出去return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));}
上面的getTarget方法将会利用@FeginClient注解的参数进行封装,然后利用jdk动态代理返回一个Object出去。
@Overridepublic <T> T newInstance(Target<T> target) {Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {continue;} else if(Util.isDefault(method)) {DefaultMethodHandler handler = new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}InvocationHandler handler = factory.create(target, methodToHandler);T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}
上面主要流程:
- 通过@EnableFeignClients注解上的@Import注入的FeignClientsRegistrar类,拿到一个扫描器,如果在EnableFeignClients注解上设置了clients属性则不扫描,如果没有设置则扫描加了@FeignClient的注解;
- 遍历所有加了@FeignClient的注解的类,每个FeignClient都会生成一个FeignClientFactoryBean的BeanDefinition,然后注册到spring容器中;
- 当客户端注入这个加了@FeignClient的类的对象时,将会调用FactoryBean的getObject方法,FactoryBean的getObject方法返回的就是当前Bean的实例;
- 然后这个Bean是将@FeignClient注解的参数利用jdk动态代理生成的。
