Feign踩坑源码分析--@FeignClient注入容器

2023-05-10 16:26:19 | 来源:博客园


(资料图)

一.@EnableFeignClients  1.1.类介绍

从上面注释可以看出是扫描声明了@FeignClient接口的类,还引入了FeignClientsRegistrar类,从字面意思可以看出是进行了FeignClient 客户端类的注册。

1.2.FeignClientsRegistrar 详解

最主要的一个方法:registerBeanDefinitions注册bean定义信息,主要功能是实现向容器注册Feign客户端配置信息和所有的使用了@FeignClient的类;

1.2.1.registerDefaultConfiguration()
private void registerDefaultConfiguration(AnnotationMetadata metadata,        BeanDefinitionRegistry registry) {    // 获取@EnableFeignClients注解的属性和值    Map defaultAttrs = metadata            .getAnnotationAttributes(EnableFeignClients.class.getName(), true);    // 获取属性中的默认配置 defaultConfiguration,name是main主程序    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {        String name;        if (metadata.hasEnclosingClass()) {            name = "default." + metadata.getEnclosingClassName();        }        else {            name = "default." + metadata.getClassName();        }        //默认配置信息进行容器注册        registerClientConfiguration(registry, name,                defaultAttrs.get("defaultConfiguration"));    }}
1.2.1.1.registerClientConfiguration
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,        Object configuration) {    // 创建一个 FeignClientSpecification.class 构造器    BeanDefinitionBuilder builder = BeanDefinitionBuilder            .genericBeanDefinition(FeignClientSpecification.class);    builder.addConstructorArgValue(name);    builder.addConstructorArgValue(configuration);    // 向容器中注册默认配置    registry.registerBeanDefinition(            name + "." + FeignClientSpecification.class.getSimpleName(),            builder.getBeanDefinition());}
1.2.1.2.registerBeanDefinition

核心代码在:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)            throws BeanDefinitionStoreException {            ............            if (hasBeanCreationStarted()) {                // Cannot modify startup-time collection elements anymore (for stable iteration)                synchronized (this.beanDefinitionMap) {                    //放到beanDefinitionMap,到AbstractApplicationContext#finishBeanFactoryInitialization(beanFactory)中取出进行bean实例化                    //此时是处于invokeBeanFactoryPostProcessors(beanFactory);阶段,进行BeanDefinitionRegistryPostProcessor的处理                    this.beanDefinitionMap.put(beanName, beanDefinition);                    List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);                    updatedDefinitions.addAll(this.beanDefinitionNames);                    updatedDefinitions.add(beanName);                    this.beanDefinitionNames = updatedDefinitions;                    removeManualSingletonName(beanName);                }            }            ..........    }
1.2.2.registerFeignClients()
public void registerFeignClients(AnnotationMetadata metadata,            BeanDefinitionRegistry registry) {        //获取一个扫描器        ClassPathScanningCandidateComponentProvider scanner = getScanner();        //设置资源路径        scanner.setResourceLoader(this.resourceLoader);        //包路径        Set basePackages;        //获取EnableFeignClients注解的参数和值,有5个值,其中clients对应的class[0]是没有值的        Map attrs = metadata                .getAnnotationAttributes(EnableFeignClients.class.getName());        //定义一个 FeignClient 注解类型过滤器        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(                FeignClient.class);        //clients为class[0],长度为0        final Class[] clients = attrs == null ? null                : (Class[]) attrs.get("clients");        if (clients == null || clients.length == 0) {            //FeignClient 注解过滤器添加到扫描器中            scanner.addIncludeFilter(annotationTypeFilter);            //获取Application对应的根路径包            basePackages = getBasePackages(metadata);        }        //clients不为空:FeignClient 注解过滤器添加到扫描器中、获取包路径        else {            final Set clientClasses = new HashSet<>();            basePackages = new HashSet<>();            for (Class clazz : clients) {                basePackages.add(ClassUtils.getPackageName(clazz));                clientClasses.add(clazz.getCanonicalName());            }            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {                @Override                protected boolean match(ClassMetadata metadata) {                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");                    return clientClasses.contains(cleaned);                }            };            scanner.addIncludeFilter(                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));        }        //遍历包路径,获取标记@FeignClient注解的接口向容器中注入        for (String basePackage : basePackages) {            Set 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");                    //获取@FeignClient的参数和值                    Map attributes = annotationMetadata                            .getAnnotationAttributes(                                    FeignClient.class.getCanonicalName());                    String name = getClientName(attributes);                    //再次更新配置                    registerClientConfiguration(registry, name,                            attributes.get("configuration"));                    //注册                    registerFeignClient(registry, annotationMetadata, attributes);                }            }        }    }
1.2.2.2.registerFeignClient
//定义一个 FeignClientFactoryBean bean定义构造器(重点)    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);    ..........    //前面根据attributes进行属性赋值后注入到Spring容器中    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
二.@FeignClient

从上面可以看到在注册客户端时注册了一个FeignClientFactoryBean(对于FactoryBean的扩展知识和案例),所以FeignClient的获取是从getObject()获取的:

2.1.getTarget()
 T getTarget() {//获取创建feign实例的工厂FeignContext context = this.applicationContext.getBean(FeignContext.class);//获取feign构造器Feign.Builder builder = feign(context);//url拼接if (!StringUtils.hasText(this.url)) {//拼接nameif (!this.name.startsWith("http")) {this.url = "http://" + this.name;}else {this.url = this.name;}//拼接paththis.url += cleanPath();//创建实例return (T) loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name, this.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);return (T) targeter.target(this, builder, context,new HardCodedTarget<>(this.type, this.name, url));}
2.1.1. feign()
//添加日志对象、编码器、解码器、解析规则器    protected Feign.Builder feign(FeignContext context) {        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);        Logger logger = loggerFactory.create(this.type);        // @formatter:off        Feign.Builder builder = get(context, Feign.Builder.class)                // required values                .logger(logger)                .encoder(get(context, Encoder.class))                .decoder(get(context, Decoder.class))                //SpringMvcContract:对RequestMapping、RequestParam、RequestHeader等注解进行解析                .contract(get(context, Contract.class));        // @formatter:on        configureFeign(context, builder);        return builder;    }
2.1.2.loadBalance()
protected  T loadBalance(Feign.Builder builder, FeignContext context,            HardCodedTarget target) {        //根据context上下文获取客户端实例,client:TraceLoadBalancerFeignClient,负载均衡        Client client = getOptional(context, Client.class);        if (client != null) {            //客户端创建            builder.client(client);            //获取HystrixTargerer            Targeter targeter = get(context, Targeter.class);            //调用HystrixTargeter#target进行实例创建            return targeter.target(this, builder, context, target);        }        throw new IllegalStateException(                "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");    }
2.1.3.HystrixTargeter#target
class HystrixTargeter implements Targeter {@Overridepublic  T target(FeignClientFactoryBean factory, Feign.Builder feign,        FeignContext context, Target.HardCodedTarget target) {    // 不是同一种类型,进入feign#target方法    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {        return feign.target(target);    }    ......}
2.1.4.Feign.Builder#target
public  T target(Target target) {      return build().newInstance(target);    }
2.1.4.1. Feign.Builder#target
public Feign build() {      //同步方法处理工厂,构建了日志级别信息      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,              logLevel, decode404, closeAfterDecode, propagationPolicy);      //解析信息:编码、解密、同步方法处理工厂      ParseHandlersByName handlersByName =          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,              errorDecoder, synchronousMethodHandlerFactory);      //new对象      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);    }
2.1.4.2.ReflectiveFeign#newInstance
@Override  public  T newInstance(Target target) {    //得到feign类的定义方法    Map nameToHandler = targetToHandlersByName.apply(target);    //定义方法存放集合    Map methodToHandler = new LinkedHashMap();    //默认方法存放集合    List defaultMethodHandlers = new LinkedList();    //对feign类方法进行遍历    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)));      }    }    //jdk动态代理创建对象    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;  }
三.总结

主要应用了FactoryBean的特性getObject()进行jdk动态创建一个ReflectiveFeign的代理对象。

上一篇 下一篇

相关新闻

Feign踩坑源码分析--@FeignClient注入容器

韩民调:过半韩国人主张日本先就历史问题道歉,再改善关系

2023毕节中考成绩查询时间及入口_全球微动态

天天实时:越秀·星耀TOD正在销售价格约为27000元/平

5月10日 14:44分 深深房A(000029)尾盘放量拉升

肯尼亚国际问题专家:美国监视盟友行为可耻 应自我反省

《北京企业高质量发展评价指标体系》发布会将于4月26日下午2点半在中关村软件园举行 环球时快讯

当前播报:玉米饼做的太稀了怎么办?

今日象是会意字还是形声字(什么是形声字,什么是象声字,什么是会意字)

青岛拟于5月31日出让3宗商住地 总起价22.21亿元 当前要闻

国六b排放标准给予部分车型半年过渡期 不满足RDE要求车辆超189万|时讯

财政部下达科技馆免费开放补助资金 全国核定预算91680万元

瓜迪奥拉谈换人:场上球员表现得很好 我不希望节奏被打乱

欧冠夺冠赔率:曼城1.61大幅领跑皇马第二,国米第三米兰垫底|天天头条

新西兰航空投资35亿美元用于机队改造,包括购买空客、波音客机_世界热文

最新新闻

Feign踩坑源码分析--@FeignClient注入容器

韩民调:过半韩国人主张日本先就历史问题道歉,再改善关系

2023毕节中考成绩查询时间及入口_全球微动态

天天实时:越秀·星耀TOD正在销售价格约为27000元/平

5月10日 14:44分 深深房A(000029)尾盘放量拉升

肯尼亚国际问题专家:美国监视盟友行为可耻 应自我反省

《北京企业高质量发展评价指标体系》发布会将于4月26日下午2点半在中关村软件园举行 环球时快讯

当前播报:玉米饼做的太稀了怎么办?

今日象是会意字还是形声字(什么是形声字,什么是象声字,什么是会意字)

青岛拟于5月31日出让3宗商住地 总起价22.21亿元 当前要闻

国六b排放标准给予部分车型半年过渡期 不满足RDE要求车辆超189万|时讯

财政部下达科技馆免费开放补助资金 全国核定预算91680万元

瓜迪奥拉谈换人:场上球员表现得很好 我不希望节奏被打乱

欧冠夺冠赔率:曼城1.61大幅领跑皇马第二,国米第三米兰垫底|天天头条

新西兰航空投资35亿美元用于机队改造,包括购买空客、波音客机_世界热文

这一晚,马莱莱真的让人快乐

世界实时:一夜醒来欠地铁600多万?官方回应

观焦点:项目成本后评估,真不简单

定西景点宣传片(定西景点) 每日速读

荒野大镖客2商贩该怎么玩(荒野大镖客2商贩怎么玩商贩职业经营心得)

汽车连接器行业深度_新能源汽车风起云涌_汽车连接器拾级而上(附下载)

您可以买到的最佳智能手表 – 2022 年 12 月 26 日更新

亏损公司的高管应该享受高薪吗?

这数据分析知识它不进脑子?10年数分师整理,一文教你记忆逻辑_全球热讯

被浪费的两小时

短讯!几次真的想让自己醉抖音版(几次真的想让自己醉)

世界要闻:万里扬:长江电新、宁银理财等多家机构于5月9日调研我司

世界快报:商洛市山阳县法官镇:乡村美景受青睐

2023佛山大湾区金属展观展攻略(门票+日程+展商)

曹尚等_对于曹尚等简单介绍