版本: OpenFeign 2.2.10.RELEASE
文章目录
原理
- 容器启动时,通过 @EnableFeignCleints 注解启动 Feign Starter 组件
- Feign Starter 在项目启动过程中注册全局配置,扫描包下所有的 @FeignClient 接口类,并进行注册 IOC 容器
- @FeignClient 接口类被注入时,通过
FeignClientFactoryBean#getObject
返回动态代理类 - 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request
- 通过 Client 携带 Request 调用远端服务返回请求响应(原请求的请求头不会再次携带)
- 通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据
启动原理
- 容器启动时,通过 @EnableFeignCleints 注解启动 Feign Starter 组件
- Feign Starter 在项目启动过程中注册全局配置,扫描包下所有的 @FeignClient 接口类,并进行注册 IOC 容器
- @FeignClient 接口类被注入时,通过
FeignClientFactoryBean#getObject
返回动态代理类
从 @EnableFeignClients 入手
@EnableFeignClients
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// ***导入注册类(注册类会将当前路径及子路径下的包全部扫描并注入到spring中)
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {...}
FeignClientsRegistrar类
- 资源加载,扫描路径下的全部类,获取全部标识了 @FeignClient 的接口
- 将注解的属性赋予并由 FactoryBean 通过反射创建bean,并放入 SpringIOC 容器内
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware
ResourceLoaderAware、EnvironmentAware 为 FeignClientsRegistrar 中两个属性 resourceLoader、environment 赋值(Spring做的)
ImportBeanDefinitionRegistrar 负责动态注入 IOC Bean,分别注入 Feign 配置类、FeignClient Bean
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
// 资源加载器,可以加载 classpath 下的所有文件
private ResourceLoader resourceLoader;
// 上下文,可通过该环境获取当前应用配置属性等
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册 @EnableFeignClients 提供的自定义配置类中的相关 Bean 实例
registerDefaultConfiguration(metadata,registry);
// ***扫描 packge,注册被 @FeignClient 修饰的接口类为 IOC Bean
registerFeignClients(metadata, registry);
}
}
registerFeignClients方法
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
// 获取到全部标注了 @FeignClient 注解的接口
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
// clients 为启动类上的@EnableFeignClients 的属性
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) { // true
// 添加所有 @FeignClient 注解的接口
scanner.addIncludeFilter(annotationTypeFilter);
// 当前包路径
basePackages = getBasePackages(metadata);
}
else {
// ...
}
for (String basePackage : basePackages) {
// 拿到所有标注了 @FeignClient 注解
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// ***将@FeignClient的属性赋予BeanDefinitionBuilder(FeignClientFactoryBean)并注入IOC bean
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
registerFeignClient方法
最终注入的是 FeignClientFactoryBean,实现了FactoryBean,Spring-refresh()-doGetBean() 获取bean的时候会通过FactoryBean#getObject
eg:扫描@Controller注入bean的时候,会先处理该类中@Resource的类,此时在获取的时候会直接调用 getObject方法(因为是FactoryBean)
FeignClientFactoryBean
- 实现了
InitializingBean
,会先执行afterPropertiesSet()
- 重点在
FactoryBean#getObject
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
// FeignContext是用于创建和管理Feign Client所依赖的各种类的工厂类。
// springboot自动装配进去的 \META-INF\spring.factories FeignAutoConfiguration
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// 获取日志工厂、编码、解码等bean
Feign.Builder builder = feign(context);
// 配置了name,没配置url,走负载均衡
if (!StringUtils.hasText(this.url)) {
// 拼接http eg:name= gulimall-third-party 则url=http://gulimall-third-party
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
// 格式化url(去除掉空格、前后的/)
this.url += cleanPath();
// ***执行负载均衡创建逻辑
return (T) loadBalance(builder, context,
// 将type name、url包装成 HardCodedTarget
new HardCodedTarget<>(this.type, this.name, this.url));
}
// 配置了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 load 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 (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
}
loadBalance()
target()
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 未配置 HystrixFeign,直接走的此分支
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
//**其他代码
}
public <T> T target(Target<T> target) {
// ***newInstance创建代理类
return build().newInstance(target);
}
// 包装为ReflectiveFeign
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);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
newInstance()
反射创建动态代理类
@Override
public <T> T newInstance(Target<T> target) {
// 将@FeignClient 接口中的方法全部封装成方法句柄(MethodHandler轻量级反射)
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
// 存放default方法的
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
// 遍历全部方法,根据类型添加到不同的 MethodHandlers中
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 {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
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;
}
执行流程
真正调用的时候,会执行反射的invoke方法 ReflectiveFeign#FeignInvocationHandler#invoke
-
如果不是equals、hashCode、toString方法,则会交由Map<Method, MethodHandler> dispatch.get().invoke()来反射执行对应的方法句柄
-
将接口的注解信息封装为 RequestTemplate(包含encode请求信息、请求方式、请求地址等)
-
失败重试策略
-
client执行http请求(此时请求头会全部丢失),按照对应的配置去解码响应数据
@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 {
// ***真正的去请求,封装request和解析response
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;
}
}
}
executeAndDecode()
-
构建真正的request
-
client(根据配置的feign的client)执行请求,执行时会将name转换为 ip + port
-
解析请求
-
状态码判断以及编码解码
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 执行所有feign请求过滤器
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options);
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
targetRequest()
执行过滤器逻辑
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
巨人的肩膀
https://zhuanlan.zhihu.com/p/346273428
https://www.cnblogs.com/crazymakercircle/p/11965726.html