本文基于openfeign2.1.x版本
分布式系统中,不同服务的沟通尤其重要,本文就基于springcloud的feign进行分析。
一.启动流程
1.先从@EnableFeignClients开始
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {}
@EnableFeignClients 引入了FeignClientsRegistrar,其他都是配置信息,说明具体逻辑在FeignClientsRegistrar中。
ImportBeanDefinitionRegistrar接口是用于来动态注册bean的,结合@import来一起使用。spring启动时,会执行ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,从而实现动态加载bean。
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 如何自定义了配置类,使用自定义的配置类
registerDefaultConfiguration(metadata, registry);
// 注册feignclient
registerFeignClients(metadata, registry);
}
}
对于自定义配置不是重点,主要看下registerFeignClients方法。
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 获取一个类路径扫描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
// 获取@EnableFeignClients的属性
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
// 自定义过滤标注了@FeignClient的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
// clients属性是用于指定只扫描哪些client对象,默认为[]
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
// 设置要扫描的注解
scanner.addIncludeFilter(annotationTypeFilter);
// 获取扫描路径,如果没有设置,则以标注@EnableFeignClients的类所在包作为起始路径
basePackages = getBasePackages(metadata);
}
else {
...
}
// 遍历扫描每个包路径
for (String basePackage : basePackages) {
// 扫描到标注了@FeignClient的BeanDefinition集合
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
// 获取FeignClient的配置信息
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// 这里也是处理@FeignClient自定义的配置
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 实际执行注册FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
registerFeignClient主要定义BeanDefinition,并将@FeignClient对应的配置写入。这里要注意的是将类设置为FeignClientFactoryBean。
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
// 设置FeignClientFactoryBean为FactoryBean
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
...
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
2.FeignClientFactoryBean
FeignClientFactoryBean实现了FactoryBean,是一个FactoryBean。
FactoryBean类,spring启动时,会调用getObject方法调用真正要实例的对象。
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
public Object getObject() throws Exception {
return getTarget();
}
}
<T> T getTarget() {
// 这里FeignContext是在FeignAutoConfiguration自动装配时加载的
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// 建造者模式,构建对象
Feign.Builder builder = feign(context);
// 没有自定义url参数
if (!StringUtils.hasText(this.url)) {
// 补个http前缀
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}else {
this.url = this.name;
}
// cleanPath添加统一前缀,如果FeignClient设置了path参数
this.url += cleanPath();
// 这里还是调用了 targeter.target
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
// 自定义url,这里补一个http开头
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
// cleanPath添加统一前缀,如果FeignClient设置了path参数
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
// 这里Targeter来源于FeignAutoConfiguration
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
Targeter对象来自于FeignAutoConfiguration配置类,当启用了Hystrix时,会创建HystrixTargeter,否则为DefaultTargeter。
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
2.DefaultTargeter
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
省略中间跳转之后,ReflectiveFeign调用newInstance。
public <T> T newInstance(Target<T> target) {
// 解析每个方法,并构建方法与MethodHandler的映射
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
// 每个Method与MethodHandler的映射
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
...
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)会构建"接口名#方法名(参数名,参数名)"的String
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// this.factory是指上文的SynchronousMethodHandler.Factory
InvocationHandler handler = this.factory.create(target, methodToHandler);
// jdk代理
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
...
return proxy;
}
create方法实际是构造了SynchronousMethodHandler。
public MethodHandler create(Target<?> target, MethodMetadata md, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404, this.closeAfterDecode, this.propagationPolicy);
}
二.执行流程
feig调用时流程:
- 先执行FeignInvocationHandler的invoke,根据method获取对应映射的MethodHandler
- 然后再执行SynchronousMethodHandler里面的invoke方法
- 组装请求数据,发起请求
- 请求返回类型为Response,直接返回。否则,解码数据。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
return dispatch.get(method).invoke(args);
}
public Object invoke(Object[] argv) throws Throwable {
// 填充请求参数
RequestTemplate template = buildTemplateFromArgs.create(argv);
...
while(true) {
try {
return this.executeAndDecode(template, options);
}catch (RetryableException var9) {
try {
// 调用异常,会执行重试机制
retryer.continueOrPropagate(e);
}
...
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 这里会调用拦截器,然后生成实际的请求对象
Request request = this.targetRequest(template);
...
Response response;
try {
// 客户端执行http调用,client由专门的http工具实现
// 默认为HttpURLConnection,一般配置为okHttp
response = this.client.execute(request, options);
}
...
// 返回类型是Response,直接返回
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;
}
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);
}
}