• spring cloud openfeign 源码


    一、读取注解信息

     入口

     1 import org.springframework.boot.SpringApplication;
     2 import org.springframework.boot.autoconfigure.SpringBootApplication;
     3 import org.springframework.cloud.openfeign.EnableFeignClients;
     4 
     5 
     6 @SpringBootApplication
     7 @EnableFeignClients
     8 public class CjsPriceServiceApplication {
     9 
    10     public static void main(String[] args) {
    11         SpringApplication.run(CjsPriceServiceApplication.class, args);
    12     }
    13 
    14 }

    spring boot 项目启动后会自动扫描application上面的注解,@EnableFeignClients的注解如下

    1 @Retention(RetentionPolicy.RUNTIME)
    2 @Target(ElementType.TYPE)
    3 @Documented
    4 @Import(FeignClientsRegistrar.class)
    5 public @interface EnableFeignClients {
    6 。。。。
    7 }

    在注解中导入了 FeignClientsRegistrar类,用来像spring注册,EnableFeignClients和FeignClient上面开发人员添加的注解信息

    1     @Override
    2     public void registerBeanDefinitions(AnnotationMetadata metadata,
    3             BeanDefinitionRegistry registry) {
    4         registerDefaultConfiguration(metadata, registry);
    5         registerFeignClients(metadata, registry);
    6     }

    二、当项目启动,读取@Autowired时会调用,实现了FactoryBean接口的FeignClientFactoryBean.getObject()方法

    1     @Override
    2     public Object getObject() throws Exception {
    3         return getTarget();
    4     }
     1 <T> T getTarget() {
     2         FeignContext context = this.applicationContext.getBean(FeignContext.class);
     3         Feign.Builder builder = feign(context);
     4 
     5         if (!StringUtils.hasText(this.url)) {
     6             if (!this.name.startsWith("http")) {
     7                 this.url = "http://" + this.name;
     8             }
     9             else {
    10                 this.url = this.name;
    11             }
    12             this.url += cleanPath();
    13             return (T) loadBalance(builder, context,
    14                     new HardCodedTarget<>(this.type, this.name, this.url));
    15         }
    16         if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
    17             this.url = "http://" + this.url;
    18         }
    19         String url = this.url + cleanPath();
    20         Client client = getOptional(context, Client.class);
    21         if (client != null) {
    22             if (client instanceof LoadBalancerFeignClient) {
    23                 // not load balancing because we have a url,
    24                 // but ribbon is on the classpath, so unwrap
    25                 client = ((LoadBalancerFeignClient) client).getDelegate();
    26             }
    27             builder.client(client);
    28         }
    29         Targeter targeter = get(context, Targeter.class);
    30         return (T) targeter.target(this, builder, context,
    31                 new HardCodedTarget<>(this.type, this.name, url));
    32     }

    可以看到 getTarget()有两种返回结果的情况,其原理都一样后来调用了 targeter.target()方法

     1 package org.springframework.cloud.openfeign;
     2 
     3 import feign.Feign;
     4 import feign.Target;
     5 
     6 /**
     7  * @author Spencer Gibb
     8  */
     9 interface Targeter {
    10 
    11     <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
    12             FeignContext context, Target.HardCodedTarget<T> target);
    13 
    14 }

    默认实现类

     1 package org.springframework.cloud.openfeign;
     2 
     3 import feign.Feign;
     4 import feign.Target;
     5 
     6 /**
     7  * @author Spencer Gibb
     8  */
     9 class DefaultTargeter implements Targeter {
    10 
    11     @Override
    12     public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
    13             FeignContext context, Target.HardCodedTarget<T> target) {
    14         return feign.target(target);
    15     }
    16 
    17 }

    然后再看 feign.target(target);方法

     1     public <T> T target(Target<T> target) {
     2       return build().newInstance(target);
     3     }
     4 
     5     public Feign build() {
     6       SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
     7           new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
     8               logLevel, decode404, closeAfterDecode, propagationPolicy);
     9       ParseHandlersByName handlersByName =
    10           new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
    11               errorDecoder, synchronousMethodHandlerFactory);
    12       return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    13     }
    14   }
    build()方法返回了创建代理类的对象,然后调用了创建代理的 newInstance方法
     1   public <T> T newInstance(Target<T> target) {
     2     Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
     3     Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
     4     List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
     5 
     6     for (Method method : target.type().getMethods()) {
     7       if (method.getDeclaringClass() == Object.class) {
     8         continue;
     9       } else if (Util.isDefault(method)) {
    10         DefaultMethodHandler handler = new DefaultMethodHandler(method);
    11         defaultMethodHandlers.add(handler);
    12         methodToHandler.put(method, handler);
    13       } else {
    14         methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    15       }
    16     }
    17     InvocationHandler handler = factory.create(target, methodToHandler);
    18     T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
    19         new Class<?>[] {target.type()}, handler);
    20 
    21     for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    22       defaultMethodHandler.bindTo(proxy);
    23     }
    24     return proxy;
    25   }

     最后,当我们项目中使用 @Autowired注入时,就回调用工厂类 FeignClientFactoryBean方法的 getObject()方法 返回我们的代理对象

  • 相关阅读:
    在HTML文件中加载js
    HTML表单的问题
    HTML自动换行的问题
    HTML的结束标签问题
    数据库基本知识
    PHP中数据库的连接
    2014年7月
    3.2版本视频讲解知识点
    2014年7月
    选项卡
  • 原文地址:https://www.cnblogs.com/yechen2019/p/11676859.html
Copyright © 2020-2023  润新知