FeignClient 构建流程

我们首先先把主流程跑一遍,回头再分析分析细节,题外说一句,自己分析下源码细节可以加深印象

首先要说下,要开启Feign,需要在主启动类上加上@EnableFeignClients

看下这个注解,会发现在Spring容器中注入了一个FeignClientsRegistrar.class

接下来,好好看看这个注册类,主要的步骤我都加上注释了:

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

    // patterned after Spring Integration IntegrationComponentScanRegistrar
    // and RibbonClientsConfigurationRegistgrar

    private ResourceLoader resourceLoader;

    private ClassLoader classLoader;

    private Environment environment;

    public FeignClientsRegistrar() {
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    //这个是该类的入口方法,在该方法中向Spring容器注入要导入的类
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //配置类包装,注入Spring容器  
        registerDefaultConfiguration(metadata, registry);
        //注册FeignClientFactoryBean 核心类
        registerFeignClients(metadata, registry);
    }

    private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //获取@EnableFeignClients注解的配置属性
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        //如果当前配置中包含defaultConfiguration属性
        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            //判断当期类是不是嵌套类或者内部类来拼接Name
            //一般都不是嵌套类 例如:name = "default."+主启动类全限定名称
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            //将该配置类注入其中
            //其实注入的是FeignClientSpecification配置类,该类的配置项为defaultConfiguration配置的属性类,name为"default."+主启动类全限定名称
            //这个后面Feign容器初始化时候要用到
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }

    public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //获取扫描器 
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        //注入类加载器
        scanner.setResourceLoader(this.resourceLoader);

        Set<String> basePackages;

        //获取注解的属性值
        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        //增加注解过滤器       
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        //获取配置的扫描类      
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        //一般都没有配置扫描类        
        if (clients == null || clients.length == 0) {
            //加入注解过滤器 过滤出含有FeignClient注解的类
            scanner.addIncludeFilter(annotationTypeFilter);
            //解析扫描路径
            basePackages = getBasePackages(metadata);
        }
        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)));
        }
        //遍历扫描路径
        for (String basePackage : basePackages) {
            //扫描路径下的含有@FeignClient注解的类 scanner中也进行过滤
            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");
                    //获取@FeginClient配置的属性
                    Map<String, Object> attributes = annotationMetadata
                            .getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());
                    //从配置属性中找到服务名称 优先级由低到高 value->name->serviceId
                    String name = getClientName(attributes);
                    //这个地方又是将@FeginClient中的配置类封装注入到Spring容器中
                    //name 就是服务名称
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                    //核心代码 注入FeignClientFactoryBean
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }
    
    //这个方法是注入FeginClint的核心工厂
    private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        //获取被@FeginClient标注的类名  
        String className = annotationMetadata.getClassName();
        //获取FeignClientFactoryBean的BeanDefinition构造器
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        validate(attributes);
        //把注解中的属性注入到BeanDefinition构造类中
        //自动拼接 "http://"
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = 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);
        //设置别名 例如ServiceA + FeignClient
        String alias = name + "FeignClient";
        //获取FeignClientFactoryBean的BeanDefinition
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

        //一般为true
        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;
        }
        
        //注册FeignClientFactoryBean的BeanDefinition
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    private void validate(Map<String, Object> attributes) {
        AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
        // This blows up if an aliased property is overspecified
        annotation.getAliasedString("name", FeignClient.class, null);
    }

    /* for testing */ String getName(Map<String, Object> attributes) {
        String name = (String) attributes.get("serviceId");
        if (!StringUtils.hasText(name)) {
            name = (String) attributes.get("name");
        }
        if (!StringUtils.hasText(name)) {
            name = (String) attributes.get("value");
        }
        name = resolve(name);
        if (!StringUtils.hasText(name)) {
            return "";
        }

        String host = null;
        try {
            String url;
            if (!name.startsWith("http://") && !name.startsWith("https://")) {
                url = "http://" + name;
            } else {
                url = name;
            }
            host = new URI(url).getHost();

        }
        catch (URISyntaxException e) {
        }
        Assert.state(host != null, "Service id not legal hostname (" + name + ")");
        return name;
    }

    private String resolve(String value) {
        if (StringUtils.hasText(value)) {
            return this.environment.resolvePlaceholders(value);
        }
        return value;
    }

    private String getUrl(Map<String, Object> attributes) {
        String url = resolve((String) attributes.get("url"));
        if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
            if (!url.contains("://")) {
                url = "http://" + url;
            }
            try {
                new URL(url);
            }
            catch (MalformedURLException e) {
                throw new IllegalArgumentException(url + " is malformed", e);
            }
        }
        return url;
    }

    private String getPath(Map<String, Object> attributes) {
        String path = resolve((String) attributes.get("path"));
        if (StringUtils.hasText(path)) {
            path = path.trim();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (path.endsWith("/")) {
                path = path.substring(0, path.length() - 1);
            }
        }
        return path;
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {

            @Override
            protected boolean isCandidateComponent(
                    AnnotatedBeanDefinition beanDefinition) {
                //如果当前类是非嵌套类    
                if (beanDefinition.getMetadata().isIndependent()) {
                    // TODO until SPR-11711 will be resolved
                    //下面这个一般不满足
                    if (beanDefinition.getMetadata().isInterface()
                            && beanDefinition.getMetadata()
                                    .getInterfaceNames().length == 1
                            && Annotation.class.getName().equals(beanDefinition
                                    .getMetadata().getInterfaceNames()[0])) {
                        try {
                            Class<?> target = ClassUtils.forName(
                                    beanDefinition.getMetadata().getClassName(),
                                    FeignClientsRegistrar.this.classLoader);
                            return !target.isAnnotation();
                        }
                        catch (Exception ex) {
                            this.logger.error(
                                    "Could not load target class: "
                                            + beanDefinition.getMetadata().getClassName(),
                                    ex);

                        }
                    }
                    return true;
                }
                return false;

            }
        };
    }
    
    //扫描路径设置 一般默认
    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        //获取EnableFeignClients中的配置属性
        Map<String, Object> attributes = importingClassMetadata
                .getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());

        Set<String> basePackages = new HashSet<>();
        // 如果配置了value 数组,依次加入扫描路径
        for (String pkg : (String[]) attributes.get("value")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        // 如果配置了basePackages 数组,依次加入扫描路径
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        // 如果配置了basePackageClasses 扫描类数组,找到类全限定名称,依次加入扫描路径
        for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }
        
        //如果都没配置,以主启动类的全限定路径为根目录进行扫描
        if (basePackages.isEmpty()) {
            basePackages.add(
                    ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }
        return basePackages;
    }
    
    private String getQualifier(Map<String, Object> client) {
        if (client == null) {
            return null;
        }
        String qualifier = (String) client.get("qualifier");
        if (StringUtils.hasText(qualifier)) {
            return qualifier;
        }
        return null;
    }

    private String getClientName(Map<String, Object> client) {
        if (client == null) {
            return null;
        }
        String value = (String) client.get("value");
        if (!StringUtils.hasText(value)) {
            value = (String) client.get("name");
        }
        if (!StringUtils.hasText(value)) {
            value = (String) client.get("serviceId");
        }
        if (StringUtils.hasText(value)) {
            return value;
        }

        throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
                + FeignClient.class.getSimpleName());
    }

    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    /**
     * Helper class to create a {@link TypeFilter} that matches if all the delegates
     * match.
     *
     * @author Oliver Gierke
     */
    private static class AllTypeFilter implements TypeFilter {

        private final List<TypeFilter> delegates;

        /**
         * Creates a new {@link AllTypeFilter} to match if all the given delegates match.
         *
         * @param delegates must not be {@literal null}.
         */
        public AllTypeFilter(List<TypeFilter> delegates) {

            Assert.notNull(delegates);
            this.delegates = delegates;
        }

        @Override
        public boolean match(MetadataReader metadataReader,
                MetadataReaderFactory metadataReaderFactory) throws IOException {

            for (TypeFilter filter : this.delegates) {
                if (!filter.match(metadataReader, metadataReaderFactory)) {
                    return false;
                }
            }

            return true;
        }
    }
}

其实没什么重要的核心方法,主要完成这几步骤:

  • 获取@FeignClient,@EnableFeignClients注解中的 configuration,defaultConfiguration的配置属性,封装成FeignClientSpecification配置类,注入Spring容器中
  • 扫描出取@FeignClient的接口,取出接口中封装的属性值,例如 url,serverId,fallback,decode404等属性,构造FeignClientFactoryBean,填充获取到的属性,将该Bean注入到Spring容器中,这里注意了啊,扫描的每个接口都会构造一个FeignClientFactoryBean,bean对应的name为接口名称,所以可以猜想下,同一个ServerId可以有多个接口,不会覆盖的!

下面我们看看注入到Spring容器的FeginClientFactoryBean是怎么初始化的?
首先看看继承实现体系:

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
        ApplicationContextAware

它实现了FactoryBean接口,那么就很有意思了,Spring在对这个类型的BeanDefinition构造实例会调用getObject()方法,我们看看getObject()方法吧,一行一行看

    FeignContext context = applicationContext.getBean(FeignContext.class);

获取Fegin的容器,这个和Ribbon获取Server上下文类似

org.springframework.cloud.netflix.feign.FeignAutoConfiguration

public class FeignAutoConfiguration {

    @Autowired(required = false)
    private List<FeignClientSpecification> configurations = new ArrayList<>();

    @Bean
    public HasFeatures feignFeature() {
        return HasFeatures.namedFeature("Feign", Feign.class);
    }

    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }
    //省略………………
}   

首先configurations集合注入,是我们上文中说的注解中配置的配置类封装实例,将该配置集合加入context中,跟进源码会发现,当前的服务对应的上下文还未构建,在获取的时候构建的,看set方法

    public void setConfigurations(List<C> configurations) {
        for (C client : configurations) {
            this.configurations.put(client.getName(), client);
        }
    }

要是一个服务对应多个接口,会出现覆盖情况啊!

这块强调下,FeginContext是实现了NamedContextFactory,所有服务共享一个NamedContextFactory,在该实例的属性中有Map<String, AnnotationConfigApplicationContext> contexts ,这里填充的是服务名称对应的上下文,那么在什么时候触发上下文的构建呢,类似Ribbon,在进行获取服务的上下文的时候进行构建,我们看看构建代码:

    protected AnnotationConfigApplicationContext createContext(String name) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        if (this.configurations.containsKey(name)) {
            for (Class<?> configuration : this.configurations.get(name)
                    .getConfiguration()) {
                context.register(configuration);
            }
        }
        for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
            if (entry.getKey().startsWith("default.")) {
                for (Class<?> configuration : entry.getValue().getConfiguration()) {
                    context.register(configuration);
                }
            }
        }
        context.register(PropertyPlaceholderAutoConfiguration.class,
                this.defaultConfigType);
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
                this.propertySourceName,
                Collections.<String, Object> singletonMap(this.propertyName, name)));
        if (this.parent != null) {
            // Uses Environment from parent as well as beans
            context.setParent(this.parent);
        }
        context.refresh();
        return context;
    }

和Ribbon一样,默认情况下,如果没有配置configuration等相关的配置类,那么默认的就是一个configuration生效了,是构造传入的

    super(FeignClientsConfiguration.class, "feign", "feign.client.name");

看看这个FeignClientsConfiguration配置类里面配置了哪些属性:

  • Decoder -> ResponseEntityDecoder 解码器
  • Encoder -> SpringEncoder 编码器
  • Contract -> SpringMvcContract 注解解释器
  • Retryer -> Retryer.NEVER_RETRY
  • FeignLoggerFactory -> DefaultFeignLoggerFactory
  • FormattingConversionService

主要配置了这些属性,这些属性后续们会用到,在FeginConext构造的时候还得看一点,它设置了父级容器

    context.setParent(this.parent);

NamedContextFactory是实现了ApplicationContextAware接口的,在setApplicationContext(ApplicationContext parent)方法中将SpringApplicationContext赋值给parent,所以FeginContext是SpringApplicationContext的子容器,这个有什么用呢,在获取Bean的时候会用到,如果子容器没有会去父级容器中寻找,那么咋们考虑一个问题,在Spring上下文中,FeignClientsConfiguration实例化得各个Bean在各个子上下文中都有一个单独实例,共用服务还是从父级容器中获取单例Bean。

回到主方法:

    Feign.Builder builder = feign(context);
    
    //子方法
    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))
                .contract(get(context, Contract.class));
        // @formatter:on

        configureFeign(context, builder);

        return builder;
    }
  • 第一步获取到一个FeignLoggerFactory,在子容器中咋们实例化的是DefaultFeignLoggerFactory,所以这里获取到的就是这个
  • 创建一个Logger对象
  • 创建一个Feign.Builder,子容器中实例化的,设置相关的解析器
  • 设置属性,有优先级

设置属性这块代码,咋们看看,是通过注入顺序来控制优先级的

    protected void configureFeign(FeignContext context, Feign.Builder builder) {
        FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
        if (properties != null) {
            //默认为true
            if (properties.isDefaultToProperties()) {
                //先注入注解属性中configuration配置的配置类
                configureUsingConfiguration(context, builder);
                //注入全局配置属性
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                //注入针对单个服务的配置属性
                configureUsingProperties(properties.getConfig().get(this.name), builder);
            } else {
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(this.name), builder);
                configureUsingConfiguration(context, builder);
            }
        } else {
            configureUsingConfiguration(context, builder);
        }
    }

从上面知道了我们配置的属性优先级由低到高是

注解的configuration配置类 -> 配置的全局属性 -> 针对单个服务配置的属性

全局属性就是这样:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full

针对单个服务配置的属性:

feign:
  client:
    config:
      ServiceA:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        decode404: false

回归主函数方法getObject(),我们接着分析

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

这个URL用来指定要发送的服务地址,但是都用Ribbon做负载均衡了,一般不配置,所以会进入该逻辑代码中:

  • 首先判断下name是否是http开头的,如果没有就加上
  • 拼接映射路径前缀,一般不配置
  • 进入loadBalance()方法

最后的核心方法就是loadbalance()方法,先看看:

    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> 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?");
    }

首先返回一个Client,它的实现类为LoadBalancerFeignClient,这个注入不在子容器中,getBean()是先优先在自己容器中寻找,如果没有再从父级容器中寻找,我们想想,如果按照这个逻辑,多个服务获取到的Clinet是父级的单例实例!

接着下一步,获取到了Client,将该LoadBalancerFeignClient注入到Builer中,之后获取Targeter,实现类是HystrixTargeter,接着又是一个方法,看看吧。

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                        Target.HardCodedTarget<T> target) {
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            return feign.target(target);
        }
        feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
        SetterFactory setterFactory = getOptional(factory.getName(), context,
            SetterFactory.class);
        if (setterFactory != null) {
            builder.setterFactory(setterFactory);
        }
        Class<?> fallback = factory.getFallback();
        if (fallback != void.class) {
            return targetWithFallback(factory.getName(), context, target, builder, fallback);
        }
        Class<?> fallbackFactory = factory.getFallbackFactory();
        if (fallbackFactory != void.class) {
            return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
        }

        return feign.target(target);
    }

首先看起来很麻烦,慢慢来吧,
首先判断feign instanceof feign.hystrix.HystrixFeign.Builder,当时说过Fegin.Builder是在子容器实例化的,我们看看实例化的类是怎么回事:

class Builder extends Feign.Builder

所以这块判断是false,直接进入 feign.target(target)方法:

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

两个方法,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);
    }
  • 使用一堆组件构造构造SynchronousMethodHandler,这个后面会知道干什么用的,核心类,动态代理主要靠这个玩的
  • 构建ParseHandlersByName
  • 使用SynchronousMethodHandler和ParseHandlersByName构建一个ReflectiveFeign

接着看看newInstance方法,这个方法是整个构建的核心,主要的动态代理是这个方法实现的

public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    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;
      } 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)));
      }
    }
    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;
  }

第一步是解析接口方法,使用SpringMVCContract解析方法参数生成SynchronousMethodHandler:
[图片上传失败...(image-f785b6-1628498535860)]
[图片上传失败...(image-42d0bb-1628498535860)]
这里面包含了方法的解析参数,其中SynchronousMethodHandler.metedata为方法的元数据,具体的是使用SpringMVCContract解析注册参数获得元数据。

下面这一步,就是很简单的将Map<name,SynchronousMethodHandler>替换为Map<Method,SynchronousMethodHandler>
主要也是为了方便使用原有方法映射封装方法。

InvocationHandler handler = factory.create(target, methodToHandler);

这一步就是创建InvocationHandler

T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

创建动态代理对象,使用的是JDK的动态代理

从这些步骤可以看出,真正执行请求方法的是封装在SynchronousMethodHandler中逻辑,后面再说

主要的构造逻辑就这些了

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容