FeignClient生成代理过程

生成代理的基本过程

1. @EnableFeignClients注解


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public@interfaceEnableFeignClients

我们使用@EnableFeignClients来开启feign的功能。从源码中看,该注解主要作用是通过@Import注解引入FeignClientsRegistrar类,追踪源码。

2. 注入类FeignClientsRegistrar.class


class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware,BeanClassLoaderAware,EnvironmentAware

该类实现了ResourceLoaderAware,BeanClassLoaderAware,EnvironmentAware接口,在解析@import注解的过程中会将感兴趣的bean注入到该类中,这里不详细展开。实现了ImportBeanDefinitionRegisterar接口,通过重写registerBeanDefinitions方法来注册默认的feign配置类和注册feignClients对应的FeignClientFactoryBean到容器当中。看一下源码:

@Override
Public 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")); // 注册到容器
}
}
// 注册feignclients,实际上是其对应的工厂bean
Public 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)的beanDefinition
Set<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方法:

// 注册
Private void registerFeignClient(BeanDefinitionRegistryregistry,
AnnotationMetadataannotationMetadata,Map<String,Object>attributes){
StringclassName=annotationMetadata.getClassName();
// 构建FeignClientFactoryBean
BeanDefinitionBuilder 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 null

beanDefinition.setPrimary(primary);
// 是否指定了别名
String qualifier=getQualifier(attributes);
if(StringUtils.hasText(qualifier)){
 alias=qualifier;
}

BeanDefinitionHolderholder=new BeanDefinitionHolder(beanDefinition,className,
newString[]{alias});
// 注册factoryBean 和 别名
BeanDefinitionReaderUtils.registerBeanDefinition(holder,registry);
}

3 FeignClientFactoryBean类


FeignClientFactoryBean 是一个工厂bean,提供了获取FeignClient代理bean的getObject方法,直接看一下方法源码:

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();
Client 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));
}
}

最后看一下是怎么生成feign代理类的,追踪源码到ReflectiveFeign。

@Override
Public <T>T newInstance(Target<T>target){
Map<String,MethodHandler>nameToHandler=targetToHandlersByName.apply(target);// 解析方法构成SynchronousMethodHandler,不包括Object的方法,静态,默认方法)
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; // 跳过object类的方法
}elseif(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); // 封装methodHandles的处理器,确定最终调用的方法
	T proxy=(T)Proxy.newProxyInstance(target.type().getClassLoader(),newClass<?>[]{target.type()},handler);// 生成代理类

for(DefaultMethodHandler defaultMethodHandler:defaultMethodHandlers){
	defaultMethodHandler.bindTo(proxy); // 默认handler绑定到代理类上
}
Return proxy; // 返回代理
}

4.番外

到这里可能会疑问,FeignContext以及构造Feign.Build中用到的几个bean是怎么来的呢?可以看包内的spring.factories文件,这里不再深入每个类解读了:
spring.factories

可能留下的疑问:
1. @Enable**注解原理,什么时候被解析?
2. FactoryBean是什么?与beanFactory的区别是什么?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值