序文
先行レビュー
主に言えばregisterBeanDefinitions()@EnableFeignClientsの方法で読む、主に存在する
対応する構成属性注釈EnableFeignClients注入、属性注釈を対応する噴射FeignClient。
最後FeignClientは、対応するBeanを生成スプリングIOC容器に注入しました。
この講義ディレクトリ
次のように内容は以下のとおりです。
- registerFeignClient()レビュー
- FeignClientFactoryBean.getObject()解析
- Feign.builder()とクライアント()論理構築
- 装うは、動的プロキシ実装の詳細を作成します
説明
元は転載元を明記してくださいする必要があり、容易ではありません!
ブログのアドレス:花はロマンチックなカウント
マイクロチャネル公共番号を:一つの枝にはロマンチックな花を数えます
ソースコード解析
registerFeignClient()レビュー
コードの次回の見直し前:
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()
、最終的なクラスは、次いでスプリングBeanDefinitionHolderに登録されているように、主機能BeanDefinitionBuilder BeanDefinitionBuilderオブジェクトのコンストラクタAbstractBeanDefinitionを構築することで、AbstractBeanDefinitionが構成されています。
春に取得beanDefinitionクラスFeignClientFactoryBeanは、クラスが実際にFeignClientFactoryBeanクラスを返されたとき。
FeignClientFactoryBean
FactoryBeanのファクトリクラスの実現として、あなたはそれがその呼び出しますときのSpringコンテキスト内のエンティティクラスごとに作成getObject()
する方法を。
FeignClientFactoryBean.getObject()解析
ここでは、直接分析FeignClientFactoryBean.getObject()
法は、これは原則装うの動的なプロキシが含まれています。
コードを見てください:
@Override
public 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
、我々は、リボンの下で比較することができSpringClientFactory
、各サービスコール、別個ILoadBalancer、のiRule、IPingので、容器から別別個のばねコンテナ、各サービスの対応上、除去することが可能です以下のような、独自のスタッフロードバランサの関連するサービス。
我々は、サービス、例えばサービスAを呼び出した場合、サービスはSpringコンテナに関連付けされ、FeignContextは、アセンブリなどロガーコンポーネント、デコーダコンポーネントは、エンコーダのようないくつかのコンポーネント、独自の独立した関連で、別の容器を表します。
私たちは見ることができます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は、種々の構成要素に対応する実際のサービスインスタンス(サービスA)であり、パッケージの構造を知ることができる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()
@Configuration
public 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.builder()ビルドプロセスを要約したものです。
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;
}
}
@Configuration
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}
到了这里就知道了,这里Feign.client默认应该就是LoadBalancerFeignClient
了。
到这继续用一张图总结下:
创建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()
方法:
@Override
public <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
。
また、これは、私たちの話をする最初のスクリーンショットが一貫デバッグであり、ここでは原則に動的プロキシオブジェクトを生成装う非常に明確になっています。
最後に、それをデバッグ、最終的に得動的プロキシオブジェクトを見て:
概要
最後に、要約チャートはふり動的プロキシ生成ルール:
- エンコーダ、デコーダ、ロガーおよび他の成分を含有する生成Feign.builder()、ならびに関連するふりクライアントapplication.properties構成情報
- 世代Feign.client()、デフォルトはLoadBalancerFeignClient
- デフォルトTargterオブジェクトを生成します。HystrixTargter
- 動的プロキシによってビルダー、クライアント、targter JDKの動的プロキシ生成は装うオブジェクト
写真概要:
宣言
:私のブログから始まるこの記事https://www.cnblogs.com/wang-mengと公共番号:ロマンチックみなさ一つramiflorous BEは、ソースを明記してください転載必要があります!
興味のパートナーは、個々の国民の少数心配することができる:一つの枝にはロマンチックな花を数えます