spring boot feign - 静态主线

本篇主要分析open feign client组件。

OpenFeign是微服务(Eureka Client)之间进行HTTP请求调用的组件。不适用于外部和服务之间的调用。

分析OpenFeign有两条主线,一静一动。静态主线,即OpenFeign配置加载,FeignClient初始化等;动态主线,即http调用。静态主线是动态主线的根基。
本文分析静态主线,从@EnableFeignClients注解——>BeanDefinition——>实例初始化

BeanDefinition

要分析feign,入手点肯定是@EnableFeignClients注解了。因为这是使用在Application上的,是所有feign的起点。下面是EnableFeignClients .java的源码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//FeignClientsRegistrar是ImportBeanDefinitionRegistrar的子类,用来处理@FeignClient注解
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
    /**
     * basePackages()属性的别名
     */
    String[] value() default {};

    /**
     * 扫描基本包的注解组件,允许string类型
     * @return 基本包名数组
     */
    String[] basePackages() default {};

    /**
     * 和上面的basePackages()一样,只允许Class类型
     * @return 基本类名数组
     */
    Class[] basePackageClasses() default {};

    /**
     * 一个给所有feign client自定义的@Configuration,能够包含@Bean的作用,例如feign.codec.Decoder,feign.codec.Encoder,feign.Contract。
     * 默认为 FeignClientsConfiguration。
     */
    Class[] defaultConfiguration() default {};

    /**
     * 所有带@FeignClient注解的类。如果不为空,关闭路径扫描机制
     * @return
     */
    Class[] clients() default {};
}

上面的代码中,FeignClientsRegistrar是ImportBeanDefinitionRegistrar的子类,Spring用ImportBeanDefinitionRegistrar来动态注册BeanDefinition。而OpenFeign通过FeignClientsRegistrar来处理@FeignClient修饰的FeignClient接口类。将这些接口类的BeanDefinition注册到Spring容器中,这样就可以使用@Autowired等方式来装载这些FeignClient接口类的Bean实例。

注册BeanDefinition

其中org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions方法是用来注册BeanDefinition的。BeanDefinition通俗点来讲,就是可以@Autowired来引用这个类或接口。

@Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
         //注册所有@EnableFeignClients提供的配置属性中相关Bean实例
        registerDefaultConfiguration(metadata, registry);
        //扫描package,注册带@FeignClient接口类的Bean信息
        registerFeignClients(metadata, registry);
    }

下面来看一下这两个方法:

private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        Map defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
      //如果EnableFeignClients配置了defaultConfiguration,才会走这里。一般不进行任何特殊配置,都是有的。如果没有,会使用默认的FeignConfiguration。
        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"));
        }
    }

入参metadata来源于Spring的importBeanDefinitionRegistrars(它是个Map)的value属性,存放着所有的注解。这个方法在下面注册到FeignClient时也会用到。

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
        //生成BeanDefinition
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        //注册到register
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }

其中BeanDefinitionRegistry是Spring框架中,用于动态注册BeanDefinition信息的接口,调用其registerBeanDefinition方法可以将BeanDefinition注册到Spring容器中,其第一个属性就是beanName,eg:default.com.example.feignclient.FeignClientApplication.FeignClientSpecification
FeignClientSpecification类实现了NamedContextFactory.Specification接口,它是OpenFeign组件实例化的重要一环,它持有自定义配置类提供的组件实例,供OpenFeign使用。SpringCloud框架使用NamedContextFactory创建一系列的运行上下文(ApplicationContext),来让对应的Specification在这些上下文中创建实例对象,使得各个上下文中的对象相互独立。

下面是上面提及的注册到register的部分代码:

@Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
        //此处断言入参不能为空,已省略
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
            //校验是否有方法覆盖或者静态工厂方法类
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                //此处为重复覆盖,上面的标志为false,此处抛错
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                //... 省略日志输出
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
                    //检查工厂Bean是否已经创建
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
            //将注册的BeanDefinition放到beanDefinitionMap中,同时也放到beanDefinitionNames中
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                     //manualSingletonNames是手动添加的类的set集合
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }
          //如果oldBeanDefinition不为空,或者是单例类,重置beanDefinitionMap和BeanDefinitionNames
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

注册FeignClient

下面是注册FeignClient:

public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
            //扫描classpath下所有的@FeignClient注解的类
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set basePackages;
        //获取EnableFeignClients注解所有的属性
        Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        //新建注解过滤器
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        final Class[] clients = attrs == null ? null
                : (Class[]) attrs.get("clients");
          //上面讲到过这个属性,如果为空,则扫描带@FeignClient注解的类
        if (clients == null || clients.length == 0) {
              //添加Filter,设置基本包路径
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = getBasePackages(metadata);
        }
        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)));
        }
      //遍历basePackages
        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();
                    
                  //获取@FeignClient中的所有属性
                    Map attributes = annotationMetadata.getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());
                       //获取@FeignClient注解中的value属性
                    String name = getClientName(attributes);
                    //注册name到注册表中
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                    //注册FeignClient
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

实例初始化

FeignClientFactoryBean是Spring实例化所有带@FeignClient注解的类的工厂类。通过其getObject()来获取Bean实例。其代码如下所示:

@Override
    public Object getObject() throws Exception {
        FeignContext context = applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);
              //默认的url为""
        if (!StringUtils.hasText(this.url)) {
            String url;
            if (!this.name.startsWith("http")) {
                url = "http://" + this.name;
            }
            else {
                url = this.name;
            }
            url += cleanPath();
            return loadBalance(builder, context, new HardCodedTarget<>(this.type,
                    this.name, url));
        }
        // ....下面测试没有走到,暂且不表

在OpenFeign中,FeignContext继承了NamedContextFactory,用于存储各类OpenFeign的组件实例。
下面说一下loadBalance方法,它负责对HTTP请求组成等:

protected  T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget target) {
        Client client = getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
            Targeter targeter = get(context, Targeter.class);
            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?");
    }

此处的targeter为org.springframework.cloud.openfeign.HystrixTargeter,而此处的build为feign.Feign.Builder,处理逻辑在org.springframework.cloud.openfeign.HystrixTargeter#target中,如下:

if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
    return feign.target(target);
}

进而进入feign.Feign.Builder#target(feign.Target)方法中:

public  T target(Target target) {
      return build().newInstance(target);
    }

下面是build方法:

public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                               logLevel, decode404);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder,
                                  errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
    }

其中contract为org.springframework.cloud.openfeign.support.SpringMvcContract,其包含Decoder、Encoder、ErrorCoder,是对参数或请求体进行编解码。所有的请求,如果没有指定,全部默认为GET请求。
下面是feign.ReflectiveFeign#newInstance方法,该方法生成一个api绑定在target上,最后会存入缓存中:

 @Override
  public  T newInstance(Target target) {
    //处理该类中每一个方法,返回结果中key,模板
    Map nameToHandler = targetToHandlersByName.apply(target);
    Map methodToHandler = new LinkedHashMap();
    List defaultMethodHandlers = new LinkedList();

    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)));
      }
    }
      //动态代理部分,该部分移到最后分析
  }

下面是feign.ReflectiveFeign.ParseHandlersByName#apply方法:

    public Map apply(Target key) {
      //获取这个接口下所有的方法,key.type()即该接口
      List metadata = contract.parseAndValidatateMetadata(key.type());
      Map result = new LinkedHashMap();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
      //有表单且没有body的模板
          buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
        } else if (md.bodyIndex() != null) {
        //有body的模板
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
        } else {
      //通用模板,一般是get用的
          buildTemplate = new BuildTemplateByResolvingArgs(md);
        }
        result.put(md.configKey(),
                   factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }

下面是feign.Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)方法:

@Override
    public List parseAndValidatateMetadata(Class targetType) {
      //检查各种属性...代码略
      Map result = new LinkedHashMap();
       //遍历所有方法
      for (Method method : targetType.getMethods()) {
      //确定该方法不是Object的方法,不是静态或者default方法
        if (method.getDeclaringClass() == Object.class ||
            (method.getModifiers() & Modifier.STATIC) != 0 ||
            Util.isDefault(method)) {
          continue;
        }
        MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
        checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s",
                   metadata.configKey());
        result.put(metadata.configKey(), metadata);
      }
      return new ArrayList(result.values());
    }

下面是org.springframework.cloud.openfeign.support.SpringMvcContract#parseAndValidateMetadata方法:

@Override
    public MethodMetadata parseAndValidateMetadata(Class targetType, Method method) {
        //将要转译的方法存储起来
        this.processedMethods.put(Feign.configKey(targetType, method), method);
        MethodMetadata md = super.parseAndValidateMetadata(targetType, method);

        RequestMapping classAnnotation = findMergedAnnotation(targetType,
                RequestMapping.class);
        if (classAnnotation != null) {
            // produces - use from class annotation only if method has not specified this
            if (!md.template().headers().containsKey(ACCEPT)) {
                parseProduces(md, method, classAnnotation);
            }

            // consumes -- use from class annotation only if method has not specified this
            if (!md.template().headers().containsKey(CONTENT_TYPE)) {
                parseConsumes(md, method, classAnnotation);
            }

            // headers -- class annotation is inherited to methods, always write these if
            // present
            parseHeaders(md, method, classAnnotation);
        }
        return md;
    }

其中Feign.configKey(targetType, method)的值格式为UserClient#getSth(String),

下面是feign.Contract.BaseContract#parseAndValidateMetadata方法:

 protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) {
      MethodMetadata data = new MethodMetadata();
    //设置返回值
      data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
      //设置方法的key,格式为UserClient#getSth(String)
      data.configKey(Feign.configKey(targetType, method));

      if(targetType.getInterfaces().length == 1) {
        processAnnotationOnClass(data, targetType.getInterfaces()[0]);
      }
    //处理类上的注解
      processAnnotationOnClass(data, targetType);


      for (Annotation methodAnnotation : method.getAnnotations()) {
    //处理方法上的注解
        processAnnotationOnMethod(data, methodAnnotation, method);
      }
      //检查方法状态 代码略
      Class[] parameterTypes = method.getParameterTypes();
      Type[] genericParameterTypes = method.getGenericParameterTypes();

      Annotation[][] parameterAnnotations = method.getParameterAnnotations();
      int count = parameterAnnotations.length;
      for (int i = 0; i < count; i++) {
        boolean isHttpAnnotation = false;
        if (parameterAnnotations[i] != null) {
          isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
        }
        if (parameterTypes[i] == URI.class) {
          data.urlIndex(i);
        } else if (!isHttpAnnotation) {
          //检查表单参数和body
          data.bodyIndex(i);
          data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
        }
      }
    //检查head和queries参数

      return data;
    }

处理类上注解部分,期间会拆分合成注解,如GetMapping拆为RequestMapping(method=GET)。会使用Proxy.newProxyInstance动态代理生成一个SynthesizedAnnotationInvocationHandler代理类,里面存储着RequestMapping注解的所有键值对。并将类上的路径放置第0位,方便取。下面是org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationOnClass代码:

@Override
    protected void processAnnotationOnClass(MethodMetadata data, Class clz) {
        if (clz.getInterfaces().length == 0) {
      //生成一个RequestMapping的代理类
            RequestMapping classAnnotation = findMergedAnnotation(clz,RequestMapping.class);
            if (classAnnotation != null) {
                // 如果在类上有指定path,则做预置前拼接,即没有/加/
                if (classAnnotation.value().length > 0) {
                    String pathValue = emptyToNull(classAnnotation.value()[0]);
                    pathValue = resolve(pathValue);
                    if (!pathValue.startsWith("/")) {
                        pathValue = "/" + pathValue;
                    }
                    data.template().insert(0, pathValue);
                }
            }
        }
    }

处理方法上的注解,对于RequestMapping的处理和类上是一样的。但是在最后会根据head属性转换为head属性,如produces会转为acceptconsumes会转为content-type,head上的其他属性也会相应的转换过去。代码为org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationOnMethod

@Override
    protected void processAnnotationOnMethod(MethodMetadata data,
            Annotation methodAnnotation, Method method) {
        if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
                .annotationType().isAnnotationPresent(RequestMapping.class)) {
            return;
        }

        RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
        // HTTP Method,设定默认为get
        RequestMethod[] methods = methodMapping.method();
        if (methods.length == 0) {
            methods = new RequestMethod[] { RequestMethod.GET };
        }
        checkOne(method, methods, "method");
        data.template().method(methods[0].name());

        // path
        checkAtMostOne(method, methodMapping.value(), "value");
        if (methodMapping.value().length > 0) {
            String pathValue = emptyToNull(methodMapping.value()[0]);
            if (pathValue != null) {
                pathValue = resolve(pathValue);
                // Append path from @RequestMapping if value is present on method
                if (!pathValue.startsWith("/")
                        && !data.template().toString().endsWith("/")) {
                    pathValue = "/" + pathValue;
                }
                data.template().append(pathValue);
            }
        }

        // produces
        parseProduces(data, method, methodMapping);

        // consumes
        parseConsumes(data, method, methodMapping);

        // headers
        parseHeaders(data, method, methodMapping);

        data.indexToExpander(new LinkedHashMap());
    }

在参数处理时,会判断参数是否在head、url、queries上,如果没有,就加到formParams中。

feign.ReflectiveFeign#newInstance中动态代理部分:

 //动态代理部分
 //在create方法中,methodToHandler对应dispatch。dispatch在最后进行分析
    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;

下面是create方法,返回的是ReflectiveFeign.FeignInvocationHandler类型:

static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }

你可能感兴趣的:(spring boot feign - 静态主线)