1.FeignClient生成代理过程
@EnableFeignClients注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(FeignClientsRegistrar.class)public@interfaceEnableFeignClients
我们使用@EnableFeignClients来开启feign的功能。从源码中看,该注解主要作用是通过@Import注解引入FeignClientsRegistrar类,追踪源码。
注入类FeignClientsRegistrar.class
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware,BeanClassLoaderAware,EnvironmentAware
该接口实现了ResourceLoaderAware,BeanClassLoaderAware,EnvironmentAware接口,在解析@import注解的过程中会将感兴趣的bean注入到该类中,这里不详细展开。实现了ImportBeanDefinitionRegisterar接口,通过重写registerBeanDefinitions方法来注册默认的feign配置类和注册feignClients对应的FeignClientFactoryBean到容器当中。看一下源码:
主要的逻辑方法在下面这个方法:
@OverridePublic void registerBeanDefinitions(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){registerDefaultConfiguration(metadata,registry); // 注册@EnableFeignClients指定的默认配置类到容器registerFeignClients(metadata,registry); //注册feignClients对应的feignClientFactoryBean到容器}
// 注册默认的配置类(FeignClientSpecification)Private void registerDefaultConfiguration(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){Map<String,Object>defaultAttrs=metadata.getAnnotationAttributes(EnableFeignClients.class.getName(),true); // 获取默认的配置类// 如果指定了配置类,对其进行注册if(defaultAttrs!=null&&defaultAttrs.containsKey("defaultConfiguration")){Stringname;if(metadata.hasEnclosingClass()){name="default."+metadata.getEnclosingClassName();}else{name="default."+metadata.getClassName();}registerClientConfiguration(registry,name,defaultAttrs.get("defaultConfiguration")); // 注册到容器}}
在registerFeignClients这个方法中,回去扫描所有带有FeignClient注解的类,遍历解析出所有带目标注解(@FeignClient)的beanDefinition,然后调用registerFeignClient生成一个代理类,实际上每一个client接口都会生成一个代理类
// 注册feignclients,实际上是其对应的工厂beanPublic void registerFeignClients(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){ClassPathScanningCandidateComponentProviderscanner=getScanner();scanner.setResourceLoader(this.resourceLoader);// 注解扫描器…AnnotationTypeFilter annotationTypeFilter=newAnnotationTypeFilter(FeignClient.class);// 设置扫描的目标注解…省略一些代码,这里代码作用就是获取@EnableFeignClients的clients属性是否为空,确定扫描的基础包路径。(不为空时,基础包路径为指定clients class的所在包路径,为空时则为@EnableFeignClients所在包的路径)…for(String basePackage:basePackages){// 解析出所有带目标注解(@FeignClient)的beanDefinitionSet<BeanDefinition>candidateComponents=scanner.findCandidateComponents(basePackage);// 遍历注册for(BeanDefinition candidateComponent:candidateComponents){if(candidateComponent instanceof AnnotatedBeanDefinition){//verify annotated class is an interface //校验注解必须加载接口上AnnotatedBeanDefinition beanDefinition=(AnnotatedBeanDefinition)candidateComponent;AnnotationMetadata annotationMetadata=beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");Map<String,Object>attributes=annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name=getClientName(attributes);registerClientConfiguration(registry,name,attributes.get("configuration")); // 注册指定的配置类// 注册client(实际上为factoryBean)registerFeignClient(registry,annotationMetadata,attributes);}}}}
主要来看一下registerFeignClient方法,为每一个BeanDefinition单独生成一个FeignClientFactoryBean,而FeignClientFactoryBean就是我们生成Feign接口代理类的关键
// 注册Private void registerFeignClient(BeanDefinitionRegistryregistry,AnnotationMetadataannotationMetadata,Map<String,Object>attributes){StringclassName=annotationMetadata.getClassName();// 构建FeignClientFactoryBeanBeanDefinitionBuilder definition=BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);validate(attributes);definition.addPropertyValue("url",getUrl(attributes));definition.addPropertyValue("path",getPath(attributes));Stringname=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;}BeanDefinitionHolderholder=new BeanDefinitionHolder(beanDefinition,className,newString[]{alias});// 注册factoryBean 和 别名BeanDefinitionReaderUtils.registerBeanDefinition(holder,registry);}
FeignClientFactoryBean类
FeignClientFactoryBean实现了FactoryBean,FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。
springCloud FeignClient其实是利用了spring的代理工厂来生成代理类,所以这里将所有的 feignClient的描述信息 BeanDefinition设定为 FeignClientFactoryBean类型,该类又继承 FactoryBean,很明显,这是一个代理类。 在spring中, FactoryBean是一个工厂bean,用作创建代理bean,所以得出结论,feign将所有的feignClient bean包装成 FeignClientFactoryBean。扫描方法到此结束。
代理类什么时候会触发生成呢? 在spring刷新容器时,当实例化我们的业务service时,如果发现注册了FeignClient,spring就会去实例化该FeignClient,同时会进行判断是否是代理bean,如果为代理bean,则调用 FeignClientFactoryBean的 T getObject() throws Exception;方法生成代理bean。
Public Object getObject()throwsException{// 获取feign的上下文FeignContext context=(FeignContext)this.applicationContext.getBean(FeignContext.class);Builder builder=this.feign(context); // 获取feign的建造者String url;if(!StringUtils.hasText(this.url)){ // 没有指定域名的,走服务发现if(!this.name.startsWith("http")){url="http://"+this.name;}else{url=this.name;}url=url+this.cleanPath();// 返回生成的代理类(负载均衡的)Return this.loadBalance(builder,context,newHardCodedTarget(this.type,this.name,url));}else{// 指定了域名的if(StringUtils.hasText(this.url)&&!this.url.startsWith("http")){this.url="http://"+this.url;}url=this.url+this.cleanPath();//生成clientClient client=(Client)this.getOptional(context,Client.class);if(client!=null){//not lod balancing because we have a url,//but ribbon is on the classpath,so unwrap // 因为指定了域名地址了所以不需要负载平衡走ribbon了if(client instanceof LoadBalancerFeignClient){client=((LoadBalancerFeignClient)client).getDelegate();}builder.client(client);}// 生成代理类Targeter targeter=(Targeter)this.get(context,Targeter.class);Return targeter.target(this,builder,context,new HardCodedTarget(this.type,this.name,url));}}
protected <T> T getOptional(FeignContext context, Class<T> type) {return context.getInstance(this.contextId, type);}
可以看到,最终是由FeignContext.getInstance来获取client
生成代理类则是通过
Return targeter.target(this,builder,context,new HardCodedTarget(this.type,this.name,url));
最终是由
@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {return feign.target(target);}
public <T> T target(Target<T> target) {return this.build().newInstance(target);}public Feign build() {Client client = (Client)Capability.enrich(this.client, this.capabilities);Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {return (RequestInterceptor)Capability.enrich(ri, this.capabilities);}).collect(Collectors.toList());Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);Options options = (Options)Capability.enrich(this.options, this.capabilities);Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}
ReflectiveFeign中实现。
下面来看一下FeignContext
FeignContext是Feign的上下文对象
@Configuration@ConditionalOnClass(Feign.class)public class FeignAutoConfiguration {@Autowired(required = false)private List<FeignClientSpecification> configurations = new ArrayList<>();@Beanpublic HasFeatures feignFeature() {return HasFeatures.namedFeature("Feign", Feign.class);}@Beanpublic FeignContext feignContext() {FeignContext context = new FeignContext();//将feign的配置类设置到feign的容器当中context.setConfigurations(this.configurations);return context;}}
可以看到feign的配置类设置到feign的容器当中,而集合中的元素 正是上面我们提到的两处调用 registerClientConfiguration方法添加进去的,前后呼应。
然而,当我们引入了 sleuth之后,获取的 feignContext确是 TraceFeignClientAutoConfiguration中配置的实例 sleuthFeignContext:
