一、大纲
还是自动装配的引子
大家都知道使用fegin,那肯定需要启用它
1、在启动类启用EnableFeignClients, 而这个直接被@import(FeignClientsRegistrar.class);
2、在FeignClientsRegistrar继承ImportBeanDefinitionRegistrar, 在这里面完成register到spring容器中。
在这个类会扫描出所有声明了@FeignClient下信息,加载这个类的路径,请求方式等注入到spring容器;注意这里是声明式接口,那么放入容器中的肯定是代理对象了。
所以这里就能想的到了,fegin他整合springmvc,hystrix,ribbon
先看看几个被自动装配的配置类
二、EnableFeignClients注入FeignClients声明接口spring容器(代理)
@EnableFeignClients
在接口注解@FeignClient(只能在接口上使用)
来看看feign怎么扫描进的spring容器中
前面我们知道了,会把所有注解过的@FeignClient的方法转成FeignClientFactoryBean注入到spring容器中。
来看看FeignClientFactoryBean这个方法类
看实现FactoryBean,从所周知,集成了FactoryBean,在spring启动的时候,会加载这个类下的集成类,并调用getObject方法
看看HystrixTargeter
三、接口调用,代理invoke
而我们在api层上直接调用接口,直接.出来的方法,就是动态代理去代用;声明式接口
看下动态代理的invoke方法
public class ReflectiveFeign extends Feign {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// 看下invoke执行了什么?
return dispatch.get(method).invoke(args);
}
}
final class SynchronousMethodHandler implements MethodHandler {
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
// 这里就很属性的Request眼熟了。具体执行还是LoadBalanceFeignClient上执行。
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 这里;spring启动只是把被@FeignClient注解的声明式接口,
// 使用动态代理注解到底spring容器里面,在.方法的,执行代理类里面的invoke;
// 重点来看下这里
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
}
}
四、集成ribbon-负载均衡
看下LoadBalanceFeignClient是怎么execute的。
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
// 这里在获取http://test-service/url/test
URI asUri = URI.create(request.url());
// 获取test-service,通过这个clientName获取具体的请求地址
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName)
// 这一步实现ribbon负载均衡
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
若有收获,就点个赞吧