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

2023-05-10 16:24:03 作者:admin

一. @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<String, Object> 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<String> 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<String> basePackages;        //获取EnableFeignClients注解的参数和值,有5个值,其中clients对应的class[0]是没有值的        Map<String, Object> 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<String> 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<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");                    //获取@FeignClient的参数和值                    Map<String, Object> 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> 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> T loadBalance(Feign.Builder builder, FeignContext context,            HardCodedTarget<T> 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> T target(FeignClientFactoryBean factory, Feign.Builder feign,        FeignContext context, Target.HardCodedTarget<T> target) {    // 不是同一种类型,进入feign#target方法    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {        return feign.target(target);    }    ......}

 2.1.4. Feign.Builder#target

    public <T> T target(Target<T> 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> T newInstance(Target<T> target) {    //得到feign类的定义方法    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);    //定义方法存放集合    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();    //默认方法存放集合    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();    //对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的代理对象。

在线咨询 拨打电话