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

FeignClientFactoryBean創(chuàng)建動態(tài)代理詳細解讀

 更新時間:2023年11月21日 09:12:20   作者:dalianpai  
這篇文章主要介紹了FeignClientFactoryBean創(chuàng)建動態(tài)代理詳細解讀,當直接進去注冊的方法中,一步步放下走,都是直接放bean的定義信息中放入值,然后轉成BeanDefinitionHolder,最后在注冊到IOC容器中,需要的朋友可以參考下

FeignClientFactoryBean創(chuàng)建動態(tài)代理

探索FeignClient的注冊流程

當直接進去注冊的方法中,一步步放下走,都是直接放bean的定義信息中放入值,然后轉成BeanDefinitionHolder,最后在注冊到IOC容器中。 具體的信息可以看下面斷點的圖。

image-20211019140122997

image-20211019140213072

在仔細看一下就會發(fā)現很奇怪,為什么此處傳入的是FeignClientFactoryBean,然后把feignclient的信息放它的里面,那我們就進去看看。

那我們就進行看一下FeignClientFactoryBean,里面的內容

image-20211019140829389

首先發(fā)現它實現了FactoryBean接口,可以返回bean的實例的工廠bean,通過實現該接口可以對bean進行一些額外的操作。此處肯定重寫了getObject方法,就是在此處,這個方法可以往容器中注入Bean的。

@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();
			}
			if (client instanceof FeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
				client = ((FeignBlockingLoadBalancerClient) 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?法中,?先從applicationContext中、也就是從Spring容器中獲取了?個FeignContext組件,應該是Feign存放?具類的?個上下?組件,然后從FeignContext中獲取到了FeignLoggerFactory組件,?路追進去發(fā)現,原來在底層也是維護了?個Spring容器的緩存Map<String, AnnotationConfigApplicationContext>。Feign在執(zhí)?時涉及到?系列的組件,所以Feign?脆為每個服務創(chuàng)建了?個Spring容器類ApplicationContext,?來存放每個服務各?所需要的組件,每個服務的這些?具類、組件都是互不影 響的,所以我們看到它在底層是通過?個Map來緩存的,key為服務名,value為服務所對應的的spring 容器ApplicationContext。

接下來我們陸續(xù)看到了其他的組件如:Decoder、Encoder、Contract等都被創(chuàng)建了出來,都設置到了Feign.Builder組件中,看來Feign.Builder應該是要為創(chuàng)建對象設置?些必要的組件信息。

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

但是發(fā)現FeignContext、FeignLoggerFactory還是Encoder、Decoder、Contract,他們都是接?

image-20211019150645159

發(fā)現在同包下面還有一個FeignClientsConfiguration配置類,那就點進去看看。

image-20211019150847945

image-20211019151005982

Decoder、Encoder、Contract、FeignLoggerFactory的實際類型都很順利的找到了。 因為Feign最終還是要發(fā)送HTTP請求,這?就難免要涉及到序列化和反序列化、這個過程就會牽扯到編碼和解碼的過程,Encoder和Decoder就派上?場了。 因為Feign?帶的注解@FeignClient、以及SpringMVC注解它們是被誰處理的呢?Contract的實現類SpringMVCContract就是來解析它們的,解析所有的注解信息、然后拼湊成?個完整的HTTP請求所需要的信息。

image-20211019144245884

在最后一行,有一個這個方法configureFeign(context, builder);,看方法名字就是配置feign,猜一猜應該是把配置文件的一些配置綁定到feign的配置類上。

protected void configureFeign(FeignContext context, Feign.Builder builder) {
		FeignClientProperties properties = this.applicationContext
				.getBean(FeignClientProperties.class);
 
		FeignClientConfigurer feignClientConfigurer = getOptional(context,
				FeignClientConfigurer.class);
		setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
 
		if (properties != null && inheritParentContext) {
			if (properties.isDefaultToProperties()) {
                               //這行默認取的是配置文件的信息。
				configureUsingConfiguration(context, builder);
                              //這行取的是yml中的默認配置
				configureUsingProperties(
						properties.getConfig().get(properties.getDefaultConfig()),
						builder);
                              //這行是yml中指定服務的配置
				configureUsingProperties(properties.getConfig().get(this.contextId),
						builder);
			}
			else {
				configureUsingProperties(
						properties.getConfig().get(properties.getDefaultConfig()),
						builder);
				configureUsingProperties(properties.getConfig().get(this.contextId),
						builder);
				configureUsingConfiguration(context, builder);
			}
		}
		else {
			configureUsingConfiguration(context, builder);
		}
	}

下圖為配置類中的信息

image-20211020101245947

下面為指定服務的yml中的信息

image-20211020101338558

image-20211020101454713

服務的配置信息優(yōu)先級是要?默認配置要?的,所以也會覆蓋默認的配置信息。

分析到這?,Feign要創(chuàng)建?個對象的所需要的信息都設置的差不多了,簡單來說就四?部分:

(1)默認的組件

(2)?定義組件

(3)默認的配置信息

(4)指定服務的配置信息

創(chuàng)建Feign的動態(tài)代理對象

然后我們就繼續(xù)放行,最終得到了Feign.Builder對象,然后我們繼續(xù)放行,來到了截圖的哪行,這里會返回一個對象。

image-20211019144325708

點進去,我們繼續(xù)分析,第一行我們直接過,但是看idea的提示類型是LoadBalancerFeignClient,LoadBalancer是ribbon的三大核心組件之一,應該是和reibbon負載均衡有關系。

image-20211019144547474

那我們就找一下它是在那個配置類被創(chuàng)建的。

image-20211020104444844

接下來,我們看到它直接從容器中獲取了?個Targeter對象,這個對象是HystrixTargeter對象,在FeignAutoConfiguration 中找到、然后直接調?target?法,如下圖所示:

image-20211020102344252

那我們就來到它的這個方法中,繼續(xù)放下放行:

image-20211020104727702

?路順勢跟到??后發(fā)現,target?法中、調?了build?法,??創(chuàng)建了SynchronousMethodHandler.Factory,翻譯起來也就是同步?法處理器的??類,具體?什么?的、?前也不是好確定,反正只知道??就是?來創(chuàng)建對象?的,然后它把這個??類放到了ParseHandlersByName中,并且new上了?個ReflectiveFeign對象并返回了。

緊接著調?了newInstance?法,看樣?是要創(chuàng)建?個對象,繼續(xù)跟進看下:

public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }
 
@Override
  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;
  }

看見 InvocationHandler handler = factory.create(target, methodToHandler);這行,基本就可以確定,是創(chuàng)建了動態(tài)代理。因為前面解析@FeignClient注解,并且封裝了一個Bean的定義信息注冊中Ioc容器中,比較ServiceAClient畢竟是一個接口,所以給每個接口創(chuàng)建了一個動態(tài)代理,然后調用接口時,去讓動態(tài)代理來執(zhí)行

image-20211019155531287

一進入到這個方法中,會看到有2個Map和一個List,那我們就先看一下第一個map,里面的數據如下圖:

image-20211019160113229

當我們點進apply方法的時候,進?到apply?法中、發(fā)現contract通過調?parseAndValidateMetadata?法得到了、List 、也就是接?中的所有?法的元數據信息,然后遍歷這些?法,通過factory創(chuàng)建MethodHandler,?這?的 factory就是我們前?在build?法中看到的SynchronousMethodHandler.Factory 。

image-20211019161015024

后面的話,會遍歷所有的nameToHandler,把它轉成methodToHandler。image-20211020110050039

最終來到了這行InvocationHandler handler = factory.create(target, methodToHandler);把上面獲取到的方法和要代理的對象都放到了FeignInvocationHandler中。

最終創(chuàng)建出了代理對象。

到此這篇關于FeignClientFactoryBean創(chuàng)建動態(tài)代理詳細解讀的文章就介紹到這了,更多相關FeignClientFactoryBean動態(tài)代理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java中List排序的3種常見方法總結

    Java中List排序的3種常見方法總結

    在Java編程中List對象的排序是一個常見的需求,List接口提供了多種排序方法,這篇文章主要給大家介紹了關于Java中List排序的3種常見方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-08-08
  • Spring中property-placeholder的使用與解析詳解

    Spring中property-placeholder的使用與解析詳解

    本篇文章主要介紹了Spring中property-placeholder的使用與解析詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • springboot開啟mybatis駝峰命名自動映射的三種方式

    springboot開啟mybatis駝峰命名自動映射的三種方式

    這篇文章給大家總結springboot開啟mybatis駝峰命名自動映射的三種方式,文章并通過代碼示例給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下
    2024-02-02
  • SpringBoot項目中使用AOP的方法

    SpringBoot項目中使用AOP的方法

    本篇文章主要介紹了SpringBoot項目中使用AOP的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • Java DOM4J方式生成XML的方法

    Java DOM4J方式生成XML的方法

    今天小編就為大家分享一篇Java DOM4J方式生成XML的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • Java數據結構之有向圖設計與實現詳解

    Java數據結構之有向圖設計與實現詳解

    有向圖是具有方向性的圖,由一組頂點和一組有方向的邊組成,每條方向的邊都連著一對有序的頂點。本文為大家介紹的是有向圖的設計與實現,需要的可以參考一下
    2022-11-11
  • Spring Boot整合swagger使用教程詳解

    Spring Boot整合swagger使用教程詳解

    這篇文章主要介紹了Spring Boot整合swagger使用教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • java 格式化時間的示例代碼

    java 格式化時間的示例代碼

    這篇文章主要介紹了java 格式化時間的示例代碼,幫助大家更好的利用Java處理時間,感興趣的朋友可以了解下
    2020-12-12
  • drools的簡單入門案例場景分析

    drools的簡單入門案例場景分析

    drools是一款由JBoss組織提供的基于Java語言開發(fā)的開源規(guī)則引擎,可以將復雜且多變的業(yè)務規(guī)則從硬編碼中解放出來,這篇文章主要介紹了drools的簡單入門案例,需要的朋友可以參考下
    2022-05-05
  • spring-cloud-gateway動態(tài)路由的實現方法

    spring-cloud-gateway動態(tài)路由的實現方法

    這篇文章主要介紹了spring-cloud-gateway動態(tài)路由的實現方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01

最新評論