欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于feign接口動態(tài)代理源碼解析

 更新時間:2022年03月09日 08:55:50   作者:keygod1  
這篇文章主要介紹了關(guān)于feign接口動態(tài)代理源碼解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

feign接口動態(tài)代理源碼解析

@FeignClinet 代理類注冊

@FeignClinet 通過動態(tài)代理實現(xiàn)的底層http調(diào)用,既然是動態(tài)代理,必然存在創(chuàng)建代理類的過程。如Proxy.newProxyInstance或者 CGlib org.springframework.cloud.openfeign 的代理類注冊實現(xiàn)如下。

首先,org.springframework.cloud.openfeign.FeignClientsRegistrar 注冊FeignClientFactoryBean到Singleton緩存中. 一個接口對應(yīng)FeignClientFactoryBean。

spring 初始化容器過程中執(zhí)行

org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject()
@Override
? ?public Object getObject() throws Exception {
? ??? ?return getTarget();
? ?}
? ?/**
? ? * @param <T> the target type of the Feign client
? ? * @return a {@link Feign} client created with the specified data and the context information
? ? */
? ?<T> T getTarget() {
? ??? ?FeignContext context = applicationContext.getBean(FeignContext.class);
? ??? ?Feign.Builder builder = feign(context);
? ??? ?if (!StringUtils.hasText(this.url)) {
? ??? ??? ?if (!this.name.startsWith("http")) {
? ??? ??? ??? ?url = "http://" + this.name;
? ??? ??? ?}
? ??? ??? ?else {
? ??? ??? ??? ?url = this.name;
? ??? ??? ?}
? ??? ??? ?url += cleanPath();
? ??? ??? ?return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
? ??? ??? ??? ??? ?this.name, 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));
? ?}

其中 getObject() 實現(xiàn)了 FactoryBean 的 getObject(),

作用是在springContext初始化時創(chuàng)建Bean實例,如果isSingleton()返回true,則該實例會放到Spring容器的單實例緩存池中。

然后是targeter.target() 如果啟用了Hystrix調(diào)用的就是

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

org.springframework.cloud.openfeign.HystrixTargeter

/**
* @param factory bean工廠
* @param feign ?feign對象的構(gòu)造類
* @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) {
? ? //關(guān)鍵方法: 解析target對象,返回key 為 feign接口的url ,value 為請求執(zhí)行類: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)));
? ? ? }
? ? }
? ? //創(chuàng)建代理類 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;
? }

至此,代理類注冊完成。

當(dāng)調(diào)用feign接口時,其實執(zhí)行的是 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);
? ? }

調(diào)用的 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 方法執(zhí)行RPC調(diào)用的邏輯。

小結(jié)一下:FeignClientsRegistrar 解析@FeignClient注解,注冊對應(yīng)的FeignClientFactoryBean–》通過FeignClientFactoryBean的getObject()方法返回代理對象 feign.ReflectiveFeign.FeignInvocationHandler

feign源碼解析

首先我要說的是springcloud沒有rpc,這就涉及rpc和微服務(wù)的區(qū)別。springcloud的模塊通信工具feign跟httpclient和okhttp是一樣的東西,都是對http請求封裝的工具,其實feign可以選擇httpclient或者okhttp作為底層實現(xiàn)(修改配置即可)。

Feign的作用

①封裝http請求,使開發(fā)人員對發(fā)送請求的過程無感知,給人一種偽rpc感覺(這也許是feign這個名字的由來吧,偽裝~)。

②feign整合ribbon和hystrix,結(jié)合eureka起到負載均衡和熔斷器、降級作用。

源碼及流程介紹

我們從@EnableFeignClients這個注解開始追蹤

我們發(fā)現(xiàn)有個@Import注解,引用FeignClientRegistrar類,跟進去看看

2個方法:①redisterDefalterConfiguration是加載配置,②registerFeignClients掃描你填寫的basepackage下的所有@FeignClient注解的接口。第一個方法沒啥好說的,我們主要看看第二個方法。

掃描完之后,把所有包含@FeignClient注解的接口都注冊到spring的beanfactory去,讓開發(fā)人員可以@Autowired來調(diào)用。這一部分代碼我就不貼了,我們只是追求feign的原理流程,太涉及spring源碼部分,我不做解釋。

=========== 以上是feign注冊流程,下面介紹拼裝request請求部分 ===========

首先,這里看ReflectiveFeign類,這個類用的是jdk的動態(tài)代理

用到代理模式肯定是在發(fā)送feign請求之前做一些操作,繼續(xù)看看請求之前做了哪些操作。

代理攔截每一個FeignClient請求,進入SynchronousMethodHandler的invoke方法,該方法調(diào)用executeAndDecode方法,這個方法看名字就知道是創(chuàng)建請求的方法,進去看看。

在該方法發(fā)送請求并且解碼,解碼分為decoder和errordecoder,這兩個都是可以重寫。這里你可能會問解碼器,那編碼器呢,feign默認(rèn)用springEncoder,同樣是可以替換成Gson等。

=========== 以上是feign的調(diào)用流程,以下是feign使用過程的坑 ===========

①feign在D版本后默認(rèn)關(guān)閉hystrix,要想傳遞請求頭,如果不用hystrix的話在feign攔截器里塞一遍就好;如果要用hystrix,那么改用信號量。

②在C版本后默認(rèn)關(guān)閉hystrix,要使用要手動開啟

③不要妄想改變feign的邏輯,因為代理模式被寫成final,無法修改

④無法在解碼器里拋自定義異常,因為feign最終會統(tǒng)一攔截,拋出一個feignexception。你想把統(tǒng)一攔截也改了,那么你可以看看第③坑。

⑤feign的重試機制,默認(rèn)是1,也就是說超時時間會變成2倍。這個可以通過配置修改。

⑥feign集成的負載均衡器ribbon,feign有個緩存,ribbon也有個緩存,會造成上線延遲,可以修改配置實現(xiàn)。

⑦feign對格式化時間處理有問題

⑧如果你是使用生產(chǎn)者提供api,并且實現(xiàn)該接口,@requestparam可以不用在實現(xiàn)類寫,但是@requestbody不寫無法映射

以上的坑都是我在實際工作中一個一個爬過來的,僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 單機redis分布式鎖實現(xiàn)原理解析

    單機redis分布式鎖實現(xiàn)原理解析

    這篇文章主要介紹了單機redis分布式鎖實現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • springboot + mybatis配置多數(shù)據(jù)源示例

    springboot + mybatis配置多數(shù)據(jù)源示例

    本篇文章主要介紹了springboot + mybatis配置多數(shù)據(jù)源示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Java動態(tài)獲取實現(xiàn)類的方式詳解

    Java動態(tài)獲取實現(xiàn)類的方式詳解

    這篇文章主要介紹了Java動態(tài)獲取實現(xiàn)類的方式詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或工作有一定的參考價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2024-01-01
  • springBoo3.0集成knife4j4.1.0的詳細教程(swagger3)

    springBoo3.0集成knife4j4.1.0的詳細教程(swagger3)

    這篇文章主要介紹了springBoo3.0集成knife4j4.1.0的詳細教程(swagger3),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • Spring實現(xiàn)Quartz自動配置的方法詳解

    Spring實現(xiàn)Quartz自動配置的方法詳解

    這篇文章主要介紹了Spring實現(xiàn)Quartz自動配置的方法詳解,如果想在應(yīng)用中使用Quartz任務(wù)調(diào)度功能,可以通過Spring Boot實現(xiàn)Quartz的自動配置,以下介紹如何開啟Quartz自動配置,以及Quartz自動配置的實現(xiàn)過程,需要的朋友可以參考下
    2023-11-11
  • java實現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮

    java實現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮

    這篇文章主要為大家詳細介紹了java實現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • SpringCloud Feign客戶端使用流程

    SpringCloud Feign客戶端使用流程

    在springcloud中,openfeign是取代了feign作為負載均衡組件的,feign最早是netflix提供的,他是一個輕量級的支持RESTful的http服務(wù)調(diào)用框架,內(nèi)置了ribbon,而ribbon可以提供負載均衡機制,因此feign可以作為一個負載均衡的遠程服務(wù)調(diào)用框架使用
    2023-01-01
  • Java Calendar類使用案例詳解

    Java Calendar類使用案例詳解

    這篇文章主要介紹了Java Calendar類使用案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Spring中的@ExceptionHandler注解統(tǒng)一異常處理詳解

    Spring中的@ExceptionHandler注解統(tǒng)一異常處理詳解

    這篇文章主要介紹了Spring中的@ExceptionHandler注解統(tǒng)一異常處理詳解,當(dāng)我們使用這個@ExceptionHandler注解時,定義一個異常的處理方法,加上@ExceptionHandler注解,這個方法就會處理類中其他方法拋出的異常,需要的朋友可以參考下
    2024-01-01
  • MybatisPlus #{param}和${param}的用法詳解

    MybatisPlus #{param}和${param}的用法詳解

    這篇文章主要介紹了MybatisPlus #{param}和${param}的用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評論