上一講主要看了@EnableFeignClients中的registerBeanDefinitions()方法,這裏面主要是
將EnableFeignClients註解對應的配置屬性注入,將FeignClient註解對應的屬性注入。html
最後是生成FeignClient對應的bean,注入到Spring 的IOC容器。spring
目錄以下:微信
registerFeignClient()回顧app
FeignClientFactoryBean.getObject()解析負載均衡
Feign.builder()及client()構建邏輯ide
建立Feign動態代理實現細節微服務
原創不易,如若轉載 請標明來源!源碼分析
博客地址:一枝花算不算浪漫
微信公衆號:壹枝花算不算浪漫post
回顧下以前的代碼: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()
方法,這裏包含着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()構建的過程:
仍是接着上面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
了。
到這繼續用一張圖總結下:
接着上面代碼,默認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一下,看下最終生成的動態代理對象:
最後用一張圖總結Feign動態代理生成的規則:
生成Feign.builder(),裏面包含Encoder、Decoder、Logger等組件,還有application.properties中相關的feign client配置信息
生成Feign.client(),默認爲LoadBalancerFeignClient
生成默認Targter對象:HystrixTargter
builder、client、targter 經過JDK動態代理生成feign動態代理對象
一張圖總結:
本文章首發自本人博客:https://www.cnblogs.com/wang-meng 和公衆號:壹枝花算不算浪漫,如若轉載請標明來源!
感興趣的小夥伴可關注我的公衆號:壹枝花算不算浪漫