spring cloud openfeign 源码实例解析

 更新时间:2019年10月15日 14:42:31   作者:挎木剑的游侠儿  
这篇文章主要介绍了spring cloud openfeign 源码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

一、读取注解信息

入口

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;


@SpringBootApplication
@EnableFeignClients
public class CjsPriceServiceApplication {

  public static void main(String[] args) {
    SpringApplication.run(CjsPriceServiceApplication.class, args);
  }

}

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
。。。。
}

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

@Override
  public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    registerDefaultConfiguration(metadata, registry);
    registerFeignClients(metadata, registry);
  }

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

   @Override
   public Object getObject() throws Exception {
     return getTarget();
   }
<T> T getTarget() {
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    if (!StringUtils.hasText(this.url)) {
      if (!this.name.startsWith("http")) {
        this.url = "http://" + this.name;
      }
      else {
        this.url = this.name;
      }
      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));
  }

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

package org.springframework.cloud.openfeign;

import feign.Feign;
import feign.Target;

/**
 * @author Spencer Gibb
 */
interface Targeter {

  <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
      FeignContext context, Target.HardCodedTarget<T> target);

}

默认实现类

package org.springframework.cloud.openfeign;

import feign.Feign;
import feign.Target;

/**
 * @author Spencer Gibb
 */
class DefaultTargeter implements Targeter {

  @Override
  public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
      FeignContext context, Target.HardCodedTarget<T> target) {
    return feign.target(target);
  }

}

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

public <T> T target(Target<T> target) {
   return build().newInstance(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);
   return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  }
 }

build()方法返回了创建代理类的对象,然后调用了创建代理的 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;
 }

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java图片上传实现代码

    Java图片上传实现代码

    这篇文章主要为大家详细介绍了Java图片上传实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • IntelliJ IDEA 2017.1.4 x64配置步骤(介绍)

    IntelliJ IDEA 2017.1.4 x64配置步骤(介绍)

    下面小编就为大家带来一篇IntelliJ IDEA 2017.1.4 x64配置步骤(介绍)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Java Feign微服务接口调用方法详细讲解

    Java Feign微服务接口调用方法详细讲解

    现如今微服务架构十分流行,而采用微服务构建系统也会带来更清晰的业务划分和可扩展性。java如果使用微服务就离不开springcloud,我这里是把服务注册到nacos上,各个服务之间的调用使用feign
    2023-01-01
  • Java微服务的打包问题解决

    Java微服务的打包问题解决

    本文主要介绍了Java微服务的打包问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 详解使用spring aop实现业务层mysql 读写分离

    详解使用spring aop实现业务层mysql 读写分离

    本篇文章主要介绍了使用spring aop实现业务层mysql 读写分离,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Java中ThreadLocal的用法和原理详解

    Java中ThreadLocal的用法和原理详解

    这篇文章主要为大家详细介绍了Java中ThreadLocal的用法和原理,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
    2023-04-04
  • Java设计模式之备忘录模式

    Java设计模式之备忘录模式

    这篇文章主要介绍了Java设计模式之备忘录模式,备忘录模式(Memento Pattern),属于行为型设计模式,目的是用于保存一个对象在某一时刻的状态,以便于在将来某个时刻根据此状态恢复该对象,需要的朋友可以参考下
    2023-12-12
  • Java中字符串常见的一些拼接方式总结

    Java中字符串常见的一些拼接方式总结

    字符串拼接是我们在Java代码中比较经常要做的事情,就是把多个字符串拼接到一起,下面这篇文章主要给大家总结介绍了关于Java中字符串常见的一些拼接方式,需要的朋友可以参考下
    2023-04-04
  • Java多线程中sleep和wait区别

    Java多线程中sleep和wait区别

    本文主要介绍了Java多线程中sleep和wait区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 谈谈Java中try-catch-finally中的return语句

    谈谈Java中try-catch-finally中的return语句

    我们知道return语句用在某一个方法中,一是用于返回函数的执行结果,二是用于返回值为void类型的函数中,仅仅是一个return语句(return ;),此时用于结束方法的执行,也即此return后的语句将不会被执行,当然,这种情况下return语句后不能再有其它的语句了
    2016-01-01

最新评论