【一塊兒學源碼-微服務】Feign 源碼二:Feign動態代理構造過程

【一塊兒學源碼-微服務】Feign 源碼二:Feign動態代理構造過程

前言

前情回顧

上一講主要看了@EnableFeignClients中的registerBeanDefinitions()方法,這裏面主要是
將EnableFeignClients註解對應的配置屬性注入,將FeignClient註解對應的屬性注入。html

最後是生成FeignClient對應的bean,注入到Spring 的IOC容器。spring

本講目錄

目錄以下:微信

  1. registerFeignClient()回顧app

  2. FeignClientFactoryBean.getObject()解析負載均衡

  3. Feign.builder()及client()構建邏輯ide

  4. 建立Feign動態代理實現細節微服務

說明

原創不易,如若轉載 請標明來源!源碼分析

博客地址:一枝花算不算浪漫
微信公衆號:壹枝花算不算浪漫post

源碼分析

registerFeignClient()回顧

回顧下以前的代碼:ui

private void registerFeignClient(BeanDefinitionRegistry registry,
		AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
	String className = annotationMetadata.getClassName();
	BeanDefinitionBuilder definition = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientFactoryBean.class);
	validate(attributes);
	definition.addPropertyValue("url", getUrl(attributes));
	definition.addPropertyValue("path", getPath(attributes));
	String name = getName(attributes);
	definition.addPropertyValue("name", name);
	definition.addPropertyValue("type", className);
	definition.addPropertyValue("decode404", attributes.get("decode404"));
	definition.addPropertyValue("fallback", attributes.get("fallback"));
	definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
	definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

	String alias = name + "FeignClient";
	AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();	boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null

	beanDefinition.setPrimary(primary);

	String qualifier = getQualifier(attributes);	if (StringUtils.hasText(qualifier)) {
		alias = qualifier;
	}

	BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,			new String[] { alias });
	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

registerFeignClient()方法中構造了一個BeanDefinitionBuilder對象,BeanDefinitionBuilder的主要做用就是構建一個AbstractBeanDefinition,AbstractBeanDefinition類最終被構建成一個BeanDefinitionHolder 而後註冊到Spring中。

beanDefinition類爲FeignClientFactoryBean,故在Spring獲取類的時候實際返回的是FeignClientFactoryBean類。

FeignClientFactoryBean做爲一個實現了FactoryBean的工廠類,那麼每次在Spring Context 建立實體類的時候會調用它的getObject()方法。

FeignClientFactoryBean.getObject()解析

這裏直接分析FeignClientFactoryBean.getObject()方法,這裏包含着Feign動態代理的原理。

先看下代碼:

@Overridepublic Object getObject() throws Exception {	// 能夠類比於ribbon中的SpringClientFactory,每一個服務都對應一個獨立的spring容器
	FeignContext context = applicationContext.getBean(FeignContext.class);	// builder中包含contract、logLevel、encoder、decoder、options等信息
	Feign.Builder builder = feign(context);	// 若是@FeignClient註解上沒有指定url,說明是要用ribbon的負載均衡
	if (!StringUtils.hasText(this.url)) {
		String url;		if (!this.name.startsWith("http")) {
			url = "http://" + this.name;
		}		else {
			url = this.name;
		}		// 這裏構建的url相似於:http://serviceA
		url += cleanPath();		return 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 lod 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 targeter.target(this, builder, context, new HardCodedTarget<>(			this.type, this.name, url));
}public <T> T getInstance(String name, Class<T> type) {	// getContext是從SpringClientContext中獲取,以前講ribbon源碼時講過
	// 一個serviceName都會有本身的一個SpringClientContext上下文信息
	AnnotationConfigApplicationContext context = getContext(name);	if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
			type).length > 0) {		// 這裏是獲取到LoadBalancerFeignClient
		return context.getBean(type);
	}	return null;
}

首先是FeignContext ,咱們能夠類比下ribbon中的SpringClientFactory, 每一個服務的調用,都有一個獨立的ILoadBalancer、IRule、IPing等等,每一個服務都對應一個獨立的spring容器,從那個獨立的容器中,能夠取出這個服務關聯的屬於本身的LoadBalancer之類的東西。

若是咱們調用一個服務的話,好比ServiceA,那麼這個服務就會關聯一個spring容器,FeignContext就表明一個獨立的容器,關聯着本身獨立的一些組件,例如Logger組件、Decoder組件、Encoder組件等等。

咱們能夠看下FeignAutoConfiguration中:

@Configuration@ConditionalOnClass(Feign.class)@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})public class FeignAutoConfiguration {	@Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();		// configurations是一個Map結構
		context.setConfigurations(this.configurations);		return context;
	}
}public class FeignContext extends NamedContextFactory<FeignClientSpecification> {	public FeignContext() {		// FeignClientsConfiguration中會加載Encoder、Decoder、Logger等組件
		super(FeignClientsConfiguration.class, "feign", "feign.client.name");
	}
}

這裏能夠知道FeignContext的結構,裏面其實就是封裝了一個服務實例(ServiceA)對應的各類組件,其中FeignClientsConfiguration是加載默認的組件信息配置類。

接下來仍是回到FeignClientFactoryBean.getObject()中,接着看feign()方法:

protected Feign.Builder feign(FeignContext context) {	// 從context中獲取到默認Logger組件:Slf4jLogger
	FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
	Logger logger = loggerFactory.create(this.type);	// 從context中找type:Feign.Builder.class 對應的組件信息
	// 而後往builder中放入各類組件信息
	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;
}protected <T> T get(FeignContext context, Class<T> type) {	// context中轉載的有Logger組件信息,這裏默認的是Slf4jLogger
	T instance = context.getInstance(this.name, type);	if (instance == null) {		throw new IllegalStateException("No bean found of type " + type + " for "
				+ this.name);
	}	return instance;
}

這裏是構造一個Feign.builder()對象,裏面仍是封裝了各類組件信息。其中Feign.builder在FeignClientsConfiguration被初始化,通常使用的是HystrixFeign.builder()

@Configurationpublic class FeignClientsConfiguration {	// 通常環境都會配置feign.hystrix.enabled = true,這裏直接看HystrixFeign.builder();
	@Configuration
	@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = false)
		public Feign.Builder feignHystrixBuilder() {			return HystrixFeign.builder();
		}
	}
}

接着看configureFeign() 方法,這個方法是讀取application.properties中的配置信息。這裏有個頗有趣的配置:

configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.name), builder);

若是咱們配置feign,先指定一個全局配置,在指定針對於某個服務的配置,那麼某個服務配置的優先級會覆蓋全局的配置。

一張圖總結下Feign.builder()構建的過程:


02_Feignå¨æ代çæ建è¿ç¨_1_-Feign.builder__æ建.jpg

Feign.builder()及client()構建邏輯

仍是接着上面getObject() 方法去分析,上面分析完了Feign.builder()的構建,下面接着看看剩下的代碼。

loadBalance(builder, context, new HardCodedTarget<>(this.type,this.name, url));

這裏形式構造了一個HardCodeTarget對象,這個對象包含了接口類型(com.barrywang.service.feign.ServiceAFeignClient)、服務名稱(ServiceA)、url地址(http://ServiceA),跟Feign.Builder、FeignContext,一塊兒,傳入了loadBalance()方法裏去。

接着查看loadBalance() 方法:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> target) {    // 這裏仍是從context中獲取feignClient數據
    Client client = getOptional(context, Client.class);    if (client != null) {
        builder.client(client);
        Targeter targeter = get(context, Targeter.class);        return targeter.target(this, builder, context, target);
    }    throw new IllegalStateException(            "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}protected <T> T getOptional(FeignContext context, Class<T> type) {    return context.getInstance(this.name, type);
}

這裏仍是從context中獲取Client.class對應的數據,咱們繼續查看FeignAutoConfiguration 類,可是並無發現Feign.client相關的數據,查看FeignAutoConfiguration的依賴,能夠找到FeignRibbonClientAutoConfiguration ,代碼以下:

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })@Configuration@AutoConfigureBefore(FeignAutoConfiguration.class)@EnableConfigurationProperties({ FeignHttpClientProperties.class })// 這裏會import三個FeignLoadBalance配置@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })public class FeignRibbonClientAutoConfiguration {    @Bean
    @Primary
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    public CachingSpringLoadBalancerFactory cachingLBClientFactory(
            SpringClientFactory factory) {        return new CachingSpringLoadBalancerFactory(factory);
    }    @Bean
    @Primary
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
        SpringClientFactory factory,
        LoadBalancedRetryPolicyFactory retryPolicyFactory,
        LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
        LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {        return new CachingSpringLoadBalancerFactory(factory, retryPolicyFactory, loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory);
    }    // Options是超時相關的配置
    @Bean
    @ConditionalOnMissingBean
    public Request.Options feignRequestOptions() {        return LoadBalancerFeignClient.DEFAULT_OPTIONS;
    }
}@Configurationclass DefaultFeignLoadBalancedConfiguration {    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory) {        return new LoadBalancerFeignClient(new Client.Default(null, null),
                cachingFactory, clientFactory);
    }
}

到了這裏就知道了,這裏Feign.client默認應該就是LoadBalancerFeignClient了。

到這繼續用一張圖總結下:


03_Feignå¨æ代çæ建è¿ç¨_2_-Feign.client__æ建.jpg

建立Feign動態代理實現細節

接着上面代碼,默認Feign.client()爲LoadBalancerFeignClient, 而後將client加入到builder中。接着繼續跟進targer相關:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
        HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);    if (client != null) {
        builder.client(client);        // 這裏又是經過Targer而後再context中獲取默認配置
        Targeter targeter = get(context, Targeter.class);        return targeter.target(this, builder, context, target);
    }    throw new IllegalStateException(            "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}protected <T> T get(FeignContext context, Class<T> type) {
    T instance = context.getInstance(this.name, type);    if (instance == null) {        throw new IllegalStateException("No bean found of type " + type + " for "
                + this.name);
    }    return instance;
}

能夠看到,這裏又是經過Targeter.class從context中獲取對應默認Targter。咱們繼續經過FeignAutoConfiguration中進行查找:

@Configuration@ConditionalOnClass(Feign.class)@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})public class FeignAutoConfiguration {    @Autowired(required = false)
    private List<FeignClientSpecification> configurations = new ArrayList<>();    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);        return context;
    }    // 若是配置了feign.hystrix.HystrixFeign 則建立HystrixTargeter
    @Configuration
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
    protected static class HystrixFeignTargeterConfiguration {        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {            return new HystrixTargeter();
        }
    }    // 若是沒有配置feign.hystrix.HystrixFeign 則建立DefaultTargeter
    @Configuration
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {            return new DefaultTargeter();
        }
    }
}

在默認狀況下,feign是和hystrix整合的,feign.hystrix.HystrixFeign會有配置,因此這裏默認Targeter使用的是HystrixTargeter, 在loadBalance()方法中執行的targeter.target()方法就是執行HystrixTargeter.target()方法:

class HystrixTargeter implements Targeter {    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                        Target.HardCodedTarget<T> target) {    // 判斷Feign.builder()類型
    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);
    }    // 最終都會執行feign.target()方法
    return feign.target(target);
}public abstract class Feign {  public static Builder builder() {    return new Builder();
  }  /**
   * Returns a new instance of an HTTP API, defined by annotations in the {@link Feign Contract},
   * for the specified {@code target}. You should cache this result.
   */
  public abstract <T> T newInstance(Target<T> target);  public static class Builder {    // 省略部分代碼


    public <T> T target(Target<T> target) {      return build().newInstance(target);
    }    public Feign build() {      // 構建一個SynchronousMethodHandler工廠
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                               logLevel, decode404);      // 構建
      ParseHandlersByName handlersByName =          new ParseHandlersByName(contract, options, encoder, decoder,
                                  errorDecoder, synchronousMethodHandlerFactory);      return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
    }
  }
}

這裏主要是build方法,構造了一個ReflectieFein對象,接着看它裏面的newInstance()方法:

@Overridepublic <T> T newInstance(Target<T> target) {    // nameToHandler是@FeignClient中的方法名對應的MethodHandler對象
    Map<String, InvocationHandlerFactory.MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = new LinkedHashMap<Method, InvocationHandlerFactory.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 {            // 將具體的method做爲map的key值
            methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
        }
    }    // JDK動態代理 返回相似於:ReflectiveFeign$FeignInvocationHandler@7642
    // methodToHandler中包含Feign.builder()、Feign.client()等信息
    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;
}

這裏就是使用了JDK動態代理,實際上返回的Feign動態代理的對象相似於:ReflectiveFeign$FeignInvocationHandler@7642

這也和咱們第一講中的debug截圖一致了,到了這裏feign動態代理對象的生成原理都已經很清楚了。

最後debug一下,看下最終生成的動態代理對象:

image.png

總結

最後用一張圖總結Feign動態代理生成的規則:

  1. 生成Feign.builder(),裏面包含Encoder、Decoder、Logger等組件,還有application.properties中相關的feign client配置信息

  2. 生成Feign.client(),默認爲LoadBalancerFeignClient

  3. 生成默認Targter對象:HystrixTargter

  4. builder、client、targter 經過JDK動態代理生成feign動態代理對象

一張圖總結:


04_Feignå¨æ代çæ建è¿ç¨_3_-åºäºJDKå¨æ代ççæåç.jpg

申明

本文章首發自本人博客:https://www.cnblogs.com/wang-meng 和公衆號:壹枝花算不算浪漫,如若轉載請標明來源!

感興趣的小夥伴可關注我的公衆號:壹枝花算不算浪漫


22.jpg

相關文章
相關標籤/搜索