Feign源码,feignclient源码解析

  Feign源码,feignclient源码解析

  

目录

假装接口动态代理源码解析@FeignClinet代理类注册假装源码解析假装的作用源码及流程介绍

 

  

feign接口动态代理源码解析

 

  

@FeignClinet 代理类注册

@FeignClinet通过动态代理实现的底层超文本传送协议(超文本传输协议的缩写)调用,既然是动态代理,必然存在创建代理类的过程。如Proxy.newProxyInstance或者cgliborg。spring框架。云。打开假装的代理类注册实现如下。

 

  首先,组织。spring框架。云。打开假装。feignclientsregistrar注册FeignClientFactoryBean到一个缓存中.一个接口对应FeignClientFactoryBean。

  弹簧初始化容器过程中执行

  org。spring框架。云。打开假装。feignclientfactorybean。getObject()@覆盖公共对象getObject()抛出异常{ return get target();}/* * * * @ param T Feign客户端的目标类型* @返回用指定数据和上下文信息创建的{@link Feign}客户端*/T T get target(){ Feign context context=应用程序上下文。getbean(假装上下文。类);假装Builder builder=feign(上下文);如果(!字符串实用程序。hastext(这个。URL)){ if(!这个。姓名。以( http ){ URL= http:// this。姓名;} else { URL=this . name } URL=clean path();return (T) loadBalance(builder,context,new HardCodedTarget(this.type,this.name,URL));} if(字符串utils。hastext(这个。网址)!这个。网址。以( http ){ this。URL= http://这。网址;}字符串URL=this。URL清理路径();client client=get可选(上下文,客户端。类);如果(客户端!=null){ if(LoadBalancerFeignClient的客户端实例){//不负载平衡因为我们有url,//但是带状物在类路径上,所以unwrap client=((LoadBalancerFeignClient)client).get delegate();} builder.client(客户端);} Targeter targeter=get(上下文,Targeter。类);return (T) targeter.target(this,builder,context,new HardCodedTarget(this.type,this.name,URL));}其中getObject()实现了接口的getObject(),

  作用是在春天语境初始化时创建豆实例,如果伊辛莱顿()返回没错,则该实例会放到弹簧容器的

  单实例缓存池中。

  然后是targeter.target() 如果启用了Hystrix调用的就是

  

org.springframework.cloud.openfeign.HystrixTargeter.target()

org.springframework.cloud.openfeign.HystrixTargeter

 

  

/*** @param factory bean工厂* @param feign  feign对象的构造类* @param context feign接口上下文,* @param target 保存了feign接口的name,url和FeignClient的Class对象***/@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.target(target)

 

  feign.Feign.Builder

  

    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() 返回一个ReflectiveFeign对象。

 

  往下看,ReflectiveFeign的newInstance方法。

  feign.ReflectiveFeign

  

@Override  public <T> T newInstance(Target<T> target) {    //关键方法: 解析target对象,返回key 为 feign接口的url ,value 为请求执行类:SynchronousMethodHandler    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)));      }    }    //创建代理类 handler ,返回对象  feign.ReflectiveFeign.FeignInvocationHandler    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;  }

至此,代理类注册完成。

 

  当调用feign接口时,其实执行的是 feign.ReflectiveFeign.FeignInvocationHandler的invoke 方法

  

@Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {      if ("equals".equals(method.getName())) {        try {          Object otherHandler =              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;          return equals(otherHandler);        } catch (IllegalArgumentException e) {          return false;        }      } else if ("hashCode".equals(method.getName())) {        return hashCode();      } else if ("toString".equals(method.getName())) {        return toString();      }        //dispatch.get(method)返回的是 SynchronousMethodHandler 对象      return dispatch.get(method).invoke(args);    }

调用的 SynchronousMethodHandler invoke 方法。

 

  feign.SynchronousMethodHandler

  

 @Override  public Object invoke(Object[] argv) throws Throwable {    RequestTemplate template = buildTemplateFromArgs.create(argv);    Retryer retryer = this.retryer.clone();    while (true) {      try {        return executeAndDecode(template);      } catch (RetryableException e) {        try {          retryer.continueOrPropagate(e);        } catch (RetryableException th) {          Throwable cause = th.getCause();          if (propagationPolicy == UNWRAP && cause != null) {            throw cause;          } else {            throw th;          }        }        if (logLevel != Logger.Level.NONE) {          logger.logRetry(metadata.configKey(), logLevel);        }        continue;      }    }  }

executeAndDecode 方法执行RPC调用的逻辑。

 

  小结一下:FeignClientsRegistrar解析@FeignClient注解,注册对应的FeignClientFactoryBean–》通过FeignClientFactoryBean的getObject()方法返回代理对象 feign.ReflectiveFeign.FeignInvocationHandler

  

 

  

feign源码解析

首先我要说的是springcloud没有rpc,这就涉及rpc和微服务的区别。springcloud的模块通信工具feign跟httpclient和okhttp是一样的东西,都是对http请求封装的工具,其实feign可以选择httpclient或者okhttp作为底层实现(修改配置即可)。

 

  

 

  

Feign的作用

①封装http请求,使开发人员对发送请求的过程无感知,给人一种伪rpc感觉(这也许是feign这个名字的由来吧,伪装~)。

 

  ②feign整合ribbon和hystrix,结合eureka起到负载均衡和熔断器、降级作用。

  

 

  

源码及流程介绍

我们从@EnableFeignClients这个注解开始追踪

 

  

 

  我们发现有个@Import注解,引用FeignClientRegistrar类,跟进去看看

  

 

  2个方法:①redisterDefalterConfiguration是加载配置,②registerFeignClients扫描你填写的basepackage下的所有@FeignClient注解的接口。第一个方法没啥好说的,我们主要看看第二个方法。

  

 

  扫描完之后,把所有包含@FeignClient注解的接口都注册到spring的beanfactory去,让开发人员可以@Autowired来调用。这一部分代码我就不贴了,我们只是追求feign的原理流程,太涉及spring源码部分,我不做解释。

  =========== 以上是feign注册流程,下面介绍拼装request请求部分 ===========

  首先,这里看ReflectiveFeign类,这个类用的是jdk的动态代理

  

 

  用到代理模式肯定是在发送feign请求之前做一些操作,继续看看请求之前做了哪些操作。

  

 

  代理拦截每一个FeignClient请求,进入SynchronousMethodHandler的invoke方法,该方法调用executeAndDecode方法,这个方法看名字就知道是创建请求的方法,进去看看。

  

 

  在该方法发送请求并且解码,解码分为decoder和errordecoder,这两个都是可以重写。这里你可能会问解码器,那编码器呢,feign默认用springEncoder,同样是可以替换成Gson等。

  

 

  =========== 以上是feign的调用流程,以下是feign使用过程的坑===========

  ①feign在D版本后默认关闭hystrix,要想传递请求头,如果不用hystrix的话在feign拦截器里塞一遍就好;如果要用hystrix,那么改用信号量。

  ②在C版本后默认关闭hystrix,要使用要手动开启

  ③不要妄想改变feign的逻辑,因为代理模式被写成final,无法修改

  ④无法在解码器里抛自定义异常,因为feign最终会统一拦截,抛出一个feignexception。你想把统一拦截也改了,那么你可以看看第③坑。

  ⑤feign的重试机制,默认是1,也就是说超时时间会变成2倍。这个可以通过配置修改。

  ⑥feign集成的负载均衡器ribbon,feign有个缓存,ribbon也有个缓存,会造成上线延迟,可以修改配置实现。

  ⑦feign对格式化时间处理有问题

  ⑧如果你是使用生产者提供api,并且实现该接口,@requestparam可以不用在实现类写,但是@requestbody不写无法映射

  以上的坑都是我在实际工作中一个一个爬过来的,仅为个人经验,希望能给大家一个参考,也希望大家多多支持盛行IT。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: