Get into the habit of writing together! This is the 18th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the event details
Diagram + source code to explain dynamic proxy to obtain FeignClient proxy object
Success always belongs to those who love to work hard
Related articles
Diagram + source code to explain how Feign injects the client into the container
Where to start your analysis
We registered a client factory class FeignClientFactoryBean when the client registered, so we obtained the FeignClient designation from this factory, so let's see how to get the FeignClient, generally when the factory is used, then The getObject method is the method to get the instance
@Override
public Object getObject() throws Exception {
// 获取目标实例
return getTarget();
}
复制代码
getTarget()
// 返回一个 feign构造器客户端,通过 feingContext
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// 获取 feign 构造器
Feign.Builder builder = feign(context);
复制代码
What things are constructed in this constructor, enter the constructor
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;
}
复制代码
Continue to look at the process behind the getTarget method, splicing the url into the address http://GOODS-APPLICATION
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
// 进行url拼接
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
// http://GOODS-APPLICATION
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
}
复制代码
In the new HardCodedTarget method, there is nothing special about the assignment. Just look at the loadBalance method.
loadBalance() method
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// 根据feignCotext 上下文获取客户端实例,获取的client是 LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
// 如果客户端不为空
if (client != null) {
// 进行客户端创建
builder.client(client);
// 获取 Targeter 操作,这个Targeter 是 HystrixTargerer
Targeter targeter = get(context, Targeter.class);
// 调用 Targeter 的target方法
return targeter.target(this, builder, context, target);
}
}
复制代码
getOptional operation
this.contextId 是 GOODS-APPLICATION,type 是 Client.class
protected <T> T getOptional(FeignContext context, Class<T> type) {
return context.getInstance(this.contextId, type);
}
复制代码
The returned client information is shown in the figure below. The CachingSpringLoadBalancerFacory in the figure is the load balancing piece to be used.
HystrixTargeter#target method
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 它两不是一种类型所以就是false 之后就是true,进入feign#target方法
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
......
}
复制代码
Feign.Builder#target method
The build method actually creates a reflection object about feign, and then calls the newInstance method
public <T> T target(Target<T> target) {
// 创建了一个 ReflectiveFeign 对象,之后调用 ReflectiveFeign#newInstance 方法
return build().newInstance(target);
}
复制代码
First enter the build method and see what operations are done in this method. In fact, it mainly returns a ReflectiveFeign object. In fact, it is related to reflection at first glance. Let's focus on the ReflectiveFeign#newInstance method.
public Feign build() {
// 同步方法处理工厂,这里面构建了一些信息
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
logger, logLevel, decode404, closeAfterDecode, propagationPolicy);
// 解析一些信息
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
// 重要的方法new了一个 ReflectiveFeign
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
复制代码
[Key points] Reflection creates a proxy instance ReflectiveFeign#newInstance
@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
复制代码
After nameToHandler is processed, it will look like the picture below. The key is the interface name + # + method name defined by us , and the value is the previously defined synchronization method handler.
Map<Method, MethodHandler> methodToHandler =
new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers =
new LinkedList<DefaultMethodHandler>();
// 遍历 com.shangde.springcloud.service.GoodsClient 的所有方法,这里面就一个方法 goods
// method public abstract org.springframework.http.ResponseEntity
// com.shangde.springcloud.service.GoodsClient.goods()
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 {
// 因为它的声明不是类也不是默认工具方法所以直接走这里
// Feign.configKey(target.type(), method)) 是 GoodsClient#goods()
// 获取的 MethodHandler 就是 SynchronousMethodHandler
methodToHandler.put(method,
nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 创建动态代理,target 是HardCodeTarget,MethodHandler 就是 SynchronousMethodHandler
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;
}
复制代码
The final returned proxy result is the following figure, which contains the interface name, the accessed service name, and the url address. In fact, it returns a feign proxy object ReflectiveFeign