上一讲主要看了@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 和公众号:壹枝花算不算浪漫,如若转载请标明来源!
感兴趣的小伙伴可关注我的公众号:壹枝花算不算浪漫