这是我参与更文挑战的第19天,活动详情查看:更文挑战
一、前言
OpenFeign
声明的接口不论是SpringMVC
还是JAX-RS
,其底层都会把接口解析成方法元数据(MethodMetadata
),再通过动态代理生成接口的代理,并基于MethodMetadata
进行Rest
调用。
背景:在 Spring Cloud
集成中。
可概括为:1-3步是创建 Feign
,4-5步是请求处理机制。
Feign
通过包扫描注入 FeignClient
的 Bean
,该源码在 FeignClientsRegistrar
类中:
-
程序启动时,会检查是否有
@EnableFeignClients
注解,如果有该注解,则开启包扫描,扫描被@FeignClient
注解的接口。 -
当程序的启动类上有
@EnableFeignClients
注解。在程序启动后,程序会通过包扫描将有@FeignClient
注解修饰的接口连同接口名和注解的信息一起取出,赋给BeanDefinitionBuilder
,然后根据BeanDefinitionBuilder
得到BeanDefinition
,最后将BeanDefinition
注入IOC
容器中。 -
注入
BeanDefinition
之后,通过JDK
的代理,当调用Feign Client
接口里面的方法时,该方法会被拦截,源码在ReflectiveFeign
类。 -
在
SynchronousMethodHandler
类进行拦截处理,会根据参数生成RequestTemplate
对象,该对象是HTTP
请求的模板 -
executeAndDecode()
方法,该方法会通过RequestTemplate
生成Request
请求对象,然后用Http Client
获取Response
,即通过Http Client
进行Http
请求来获取响应
二、直接怼源码
发现之前看源码,是画类似流程图,但感觉不是特别好,也不方便之后回顾,这次试试时序图。
创建 Feign
时序图,如下:
步骤:
@EnableFeignClients
注解提供的包名与@FeignClient
注解修饰的接口找到所有的接口- 基于这些接口构造
FeignClientFactoryBean
这个FactoryBean
FactoryBean
内部真正构造的对象是一个Proxy
,这个Proxy
是通过Targeter#target
构造出来的。Targeter
内部构造通过Feign.Builder#build
方法完成,build
方法返回的一个Feign
对象。
先从 @EnableFeignClients
入手:
// 定位:org.springframework.cloud.openfeign
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 重要:导入 Bean
// 那么这个 FeignClientsRegistrar 肯定是处理注册的
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
... ...
}
再看 FeignClientsRegistrar
:
// 定位:org.springframework.cloud.openfeign
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
// Spring 会注入参数,并调用此方法
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 1. 注册默认配置
registerDefaultConfiguration(metadata, registry);
// 2. 扫描 @FeignClient 并注册
registerFeignClients(metadata, registry);
}
// 1. 注册默认配置
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 获取 @EnableFeignClients 这个注解的所有属性的值
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
// 获取 Application 启动类的全限定名:
// default.cn.donald.dolphin.oceanfile.OceanFileApplication
// ...
// 在 BeanDefinitionRegistry 中注册
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
// 2. 扫描 @FeignClient 并注册
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 2.1 获取 scanner:用于在指定的路径中,扫描指定条件相关的 Bean 类
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
// 2.2
// ... 一大坨代码:用于扫描指定包路径,此时会有 filter 对指定过滤
// 2.3 处理包路径
for (String basePackage : basePackages) {
// 这是扫描出来的候选 BeanDeifinition,即包含了@FeignClient注解的接口
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 2.3.1 校验是否是接口 (interface)
// 2.3.2 获取 @FeignClient 中的属性
// ...
// 2.3.3 注册配置
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 2.3.4 注册这个 feignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
// 2.3.4 注册这个 feignClient
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
// 重要:
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
// ... 一坨配置设置
// ... 注册进 Spring 容器中
}
}
来看下 FeignClientFactoryBean
:
// 定位:org.springframework.cloud.openfeign
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
// 重载方法,入口就在此:
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
// ... 一坨 URL,http 处理
// target 明显动态代理:
// 先获取实例 instance
Targeter targeter = get(context, Targeter.class);
// 再生成对应
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
}
targeter.target()
默认走 DefaultTargeter
:
// 定位:org.springframework.cloud.openfeign
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context,
Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
// 进入 feign 类
// 定位:feign
public abstract class Feign {
// 1
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
// 2. 走子类的 ReflectiveFeign
public abstract <T> T newInstance(Target<T> target);
}
// 默认情况下返回的是 `ReflectiveFeign` 这个 `Feign` 对象的子类
// 进入 ReflectiveFeign 类
// 定位:feign
public class ReflectiveFeign extends Feign {
@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler
= targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler
= new LinkedHashMap<Method, 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 {
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;
}
}
从这段代码可以看到,InvocationHandler
、Proxy
这些 JDK
内置的动态代理类完成了这个操作。