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


    一. @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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    T getTarget() {
        //获取创建feign实例的工厂
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        //获取feign构造器
        Feign.Builder builder = feign(context);
        //url拼接
        if (!StringUtils.hasText(this.url)) {
            //拼接name
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            }
            else {
                this.url = this.name;
            }
            //拼接path
            this.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 unwrap
                client = ((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 {
    
    @Override
    public  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的代理对象。

  • 相关阅读:
    机器学习---决策树的划分依据(熵、信息增益、信息增益率、基尼值和基尼指数)
    网络相关linux指令等储备
    ZZ308 物联网应用与服务赛题第F套
    云服务器安装 redis
    21天学习挑战赛——Python操作XML文件
    UNIX 域协议(本地通信协议)
    普中51单片机学习(8*8LED点阵)
    NeRF~
    Avatar虚拟形象解决方案,趣味化的视频拍摄与直播新体验
    编程学:关于同类词的等长拼写问题
  • 原文地址:https://www.cnblogs.com/huangrenhui/p/17182894.html