1. 配置类
这里有五个配置类,但是初步阅读源码的话其实只需要关注第二个和第五个配置类就行。
1.1 FeignAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,
FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
@Configuration(proxyBeanMethods = false)
@Conditional(FeignCircuitBreakerDisabledConditions.class)
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
......
}
这个配置类主要关注 FeignContext
和在开启 Feign
熔断开关情况下的用来生成代理对象的 Targeter
。
1.2 FeignLoadBalancerAutoConfiguration
Feign
中关于负载均衡的配置:
@ConditionalOnClass(Feign.class)
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter({ BlockingLoadBalancerClientAutoConfiguration.class, LoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
@Import({ HttpClientFeignLoadBalancerConfiguration.class, OkHttpFeignLoadBalancerConfiguration.class,
HttpClient5FeignLoadBalancerConfiguration.class, DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {
}
这个配置类仅仅是使用 @Import
注解导入多个配置类,导入的配置类都根据条件在声明负载均衡要使用到的 Client
的 Bean
,这里先看它的默认配置类。
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(LoadBalancerProperties.class)
class DefaultFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, properties,
loadBalancerClientFactory);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnBean(LoadBalancedRetryFactory.class)
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
return new RetryableFeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient,
loadBalancedRetryFactory, properties, loadBalancerClientFactory);
}
}
在重试开关关闭的情况下,默认注入的 Client
类型是 FeignBlockingLoadBalancerClient
,开启的开启下类型是 RetryableFeignBlockingLoadBalancerClient
,怎么样,是不是和 loadBalancer
很像。
2. @EnableFeignClients 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
/**
* 标有 @FeignClient 注解的类
*/
Class<?>[] clients() default {}
}
注解里面导入了一个 FeignClientsRegistrar
,看名字应该是 ImportBeanDefinitionRegistrar
的实现类。
1.2 FeignClientsRegistrar
主要看它的 registerBeanDefinitions
方法。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
}
1.2.1 registerDefaultConfiguration - 全局
注册默认配置(和 loadBalancer
的 LoadBalancerClientConfigurationRegistrar
类似),为每个 Feign
客户端注册指定默认配置(即注解中 defaultConfiguration
指定的配置类)。
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
}
}
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
注意 registerClientConfiguration
方法中注册的 BeanDefinition
类型是 FeignClientSpecification
,看名字是不是很熟悉,和 loadBalancer
类似,与 NamedContextFactory
相关,起到服务隔离的作用。后续会在自动配置类见到。
1.2.2 registerFeignClients - 单独
前面的 registerDefaultConfiguration
是为每个 Feign
客户端添加统一配置,那这个就是为每个 Feign
客户端添加属于自己的配置。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
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"));
// 注册Feign 客户端
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
如果
@EnableFeignClients
注解上属性clients
对应的值为空, 则进行包扫描,扫描出包含@FeignClient
的类,包扫描的路径:value
->basePackages
->basePackageClasses
,如果这几个属性对应的值都为空,则取@EnableFeignClients
注解所在类的包。如果不为空,则只取
clients
值对应的类。候选类取出完成后,先判断是否是接口,因为
@FeignClient
注解只能加在接口上。解析
Feign
客户端中@FeignClient
注解的相关属性,然后对客户端进行专属配置。registerClientConfiguration
前面讲过,主要注意一下registerFeignClient
方法。为扫描到的每一个Feign
客户端接口注册一个beanDefinition
实例,其中beanClass
为FeignClientFactoryBean
。
FactoryBean
是什么?在
Spring
中,对于Bean
的类型,一般有两种设计:普通 Bean 、工厂 Bean 。普通Bean
就是我们平常使用的@Component
、@Bean
这种方法创建的Bean
;但是考虑到一些特殊的设计:Bean
的创建需要指定一些策略,或者依赖特殊的场景来分别创建,也或者一个对象的创建过程太复杂。这种情况下,如果还是使用普通的创建Bean
方式,可能无法实现,所以Spring
提供了FactoryBean
来使用工厂方法创建对象。
public interface FactoryBean<T> {
// 返回创建的对象
@Nullable
T getObject() throws Exception;
// 返回创建的对象的类型(即泛型类型)
@Nullable
Class<?> getObjectType();
// 创建的对象是单实例Bean还是原型Bean,默认单实例
default boolean isSingleton() {
return true;
}
}
FactoryBean
本身是一个接口,如果 Bean 实现了 FactoryBean
接口,则它本身将不再是一个普通的 Bean ,不会在实际的业务逻辑中起作用,而是由创建的对象来起作用。创建 Bean
的过程由接口中的方法 getObject
实现,且 FactoryBean
生产 Bean 的机制是延迟生产。 当使用 @Autowried
或者其他方式注入 Feign
客户端的实例时,就会调用这个 getObject()
方法获取。
下面有一个简单的例子,根据这个例子来 debug
源码。
@FeignClient(name = "user-service")
public interface UserFeign {
@GetMapping("/user")
User getUser(@RequestParam("userId") String userId);
}
@Component
public class xxxx {
@Autowried
private UserFeign userFeign;
}
1.2.2.1 FeignClientFactoryBean
当在某个类注入 UserFeign
的时候,就会调用 FeignClientFactoryBean
的 getObject
方法生成对应的代理对象。
// FeignClientFactoryBean
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
// 获取上下文对象
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
// logger....
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
applyBuildCustomizers(context, builder);
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
1. FeignContext
从 IOC
容器中获取 FeignContext
,FeignContext
继承了 NamedContextFactory
,用来统一维护 Feign
中各个 Feign
客户端相互隔离的上下文。注册的地方则是在 FeignAutoConfiguration
。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,FeignEncoderProperties.class })
public class FeignAutoConfiguration {
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
}
FeignContext
的初始化就用到了前面提到过的 FeignClientSpecification
。
2. url 判断
如果 @FeignClient
注解上的 url
属性为空,则走负载均衡,生成有负载均衡的代理类。
if (!StringUtils.hasText(url)) {
// logger....
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
例如现在有两个服务 business-service
、order-service
,business-service
使用 Feign
调用 order-service
的接口,对应的 Feign
客户端上注解配置为:
@FeignClient(value = "user-service")
那么按照上面的逻辑,最终生成的 url
为 http://order-service
。如果 @FeignClient
中还指定是 path
的值的话,最终生成的 url
就为 http://order-service
拼接上 path
属性对应的值。
3. 负载均衡生成的代理类
进入 loadBalance
方法:
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
applyBuildCustomizers(context, builder);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
}
protected <T> T getOptional(FeignContext context, Class<T> type) {
return context.getInstance(contextId, type);
}
首先从当前 Feign
客户端的 AnnotationConfigApplicationContext
上下文中取出对应的类为 Client
的 Bean
,并为当前的 Feign
客户端使用,注入的地方在前面讲到的 1.2 章节处,默认的类型是 FeignBlockingLoadBalancerClient
。
public interface Client {
Response execute(Request var1, Options var2) throws IOException;
}
Client
是一个接口,里面仅包含一个方法,就是发送请求,和 LoadBalancerClient
类似。
Targeter
默认的类型是 DefaultTargeter
,我们跟进看一下生成代理对象的 target
方法:
// DefaultTargeter
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) {
// 调用 Feign.Builder 的 target 方法
return feign.target(target);
}
// Feign.Builder
public <T> T target(Target<T> target) {
return this.build().newInstance(target);
}
public Feign build() {
.....
InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
// 参数的编码器,接口含有参数注解@QueryMap时使用
QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
// handlersByName将所有参数进行封装,并提供解析接口方法的逻辑
ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
ReflectiveFeign
的构造方法有三个参数:
handlersByName
: 将builder所有参数进行封装,并提供解析接口方法的逻辑;invocationHandlerFactory
:Java
动态代理的工厂;queryMapEncoder
:参数的编码器,接口含有参数注解@QueryMap时使用。
最后调用 newInstance
方法真正生成代理对象:
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;
}
3.1 ParseHandlersByNam
解析接口方法上的注解,并将其转换成对应的处理器(handler)。而其中又用到了 Contarct
接口来解析接口中的方法,生成方法的元数据。
// ParseHandlersByName
public Map<String, MethodHandler> apply(Target target) {
// 调用 BaseContract 的 parseAndValidateMetadata 方法
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
.....
}
// BaseContract
public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
final MethodMetadata data = new MethodMetadata();
.....
final Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
for (final Method method : targetType.getMethods()) {
// 方法过滤
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
// 调用子类 SpringMvcContract
final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
......
result.put(metadata.configKey(), metadata);
}
return new ArrayList<>(result.values());
}
// SpringMvcContract
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
processedMethods.put(Feign.configKey(targetType, method), method);
// 回到 BaseContract
return super.parseAndValidateMetadata(targetType, method);
}
// BaseContract
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
...... // 构造接口中方法对应的 MethodMetadata
if (targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
processAnnotationOnClass(data, targetType);
// 处理方法上的注解
for (final Annotation methodAnnotation : method.getAnnotations()) {
processAnnotationOnMethod(data, methodAnnotation, method);
}
.....
}
注意 BaseContract
中 new MethodMetadata()
的地方,有个属性名为 template
的对象在初始化是就被设置为 RequestTemplate
,而 FeignClient
的请求发送就是通过这个类实现的。
public final class MethodMetadata implements Serializable {
private final RequestTemplate template = new RequestTemplate();
MethodMetadata() {
template.methodMetadata(this);
}
}
但是这里有一点要注意哦,我们来看下 MethodMetadata
初始化后其中的 RequestTemplate
对象:
虽然其中的 RequestTemplate
已被创建完成,但是它的 toString
方法现在确有异常提示。这是为什么呢?
public String toString() {
return request().toString();
}
public Request request() {
if (!this.resolved) {
throw new IllegalStateException("template has not been resolved.");
}
return Request.create(this.method, this.url(), this.headers(), this.body, this);
}
原来如此,toString
方法里面实际调用的 Request
对象的 toString
方法,且构造 Request
前还有一个判断条件 resolved
,只有等 RequestTemplate
被处理完成后,才能正确显示。
那什么时候
RequestTemplate
才能被处理完成呢?其实要等到代理类被调用的时候(即调用UserFeign
的getUser
方法时),才能被处理完成,具体讲解放到了后面分析代理类调用过程的章节。
先把断点打到 SpringMvcContract
类,看下 processedMethods
存放的是什么:
根据内容可以知道 processedMethods
这个 Map
中的数据:key
为接口类名+方法名+参数类型,value
为对应的 Method
对象。
然后我们看下 processAnnotationOnClass
和 processAnnotationOnMethod
分别干了什么:
// SpringMvcContract
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
if (classAnnotation != null) {
LOG.error("Cannot process class: " + clz.getName()
+ ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");
throw new IllegalArgumentException("@RequestMapping annotation not allowed on @FeignClient interfaces");
}
}
processAnnotationOnClass
是用来检查标有 @FeignClient
注解的接口是否含有 @RequestMapping
注解,如果有的话,直接抛出异常,因为这个 FeignClient
是用来调用其他服务的,自身服务如果有的 @RequestMapping
注解的话,相当于自身服务提供接口,这是不合理的。
// SpringMvcContract
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
.........
// 获取接口中包含 @RequestMapping 相关注解的方法
RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
.........
// 为 RequestTemplate 设置方法请求类型
data.template().method(Request.HttpMethod.valueOf(methods[0].name()));
// path
checkAtMostOne(method, methodMapping.value(), "value");
if (methodMapping.value().length > 0) {
String pathValue = emptyToNull(methodMapping.value()[0]);
if (pathValue != null) {
pathValue = resolve(pathValue);
// Append path from @RequestMapping if value is present on method
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().uri(pathValue, true);
if (data.template().decodeSlash() != decodeSlash) {
data.template().decodeSlash(decodeSlash);
}
}
}
......
}
这段代码就是处理 @RequestMapping
注解中各个属性,最终都是将属性对应的值放到 RequestTemplate
中。
将方法转换为 MethodMetadata
后,就需要为每个方法设置对应的 MethodHandler
。
// ParseHandlersByName
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
// 针对不同参数设置不同的 buildTemplate
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate =
new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
result.put(md.configKey(), factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
}
比如例子中使用的是 @RequestParam
,则为 BuildTemplateByResolvingArgs
;如果使用的是 @RequestBody
,则为 BuildEncodedTemplateFromArgs
。
接下来看一下生成 MethodHandler
的逻辑,使用了一个 factory
来创建:
// SynchronousMethodHandler.Factory
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,logLevel, md, buildTemplateFromArgs, options, decoder,errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
}
直接是 new
了一个 SynchronousMethodHandler
对象。
apply
方法执行完毕后,回到 newInstance
方法中:
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>();
......
// 生成代理对象
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;
}
终于看到真正生成代理对象的地方了,而且用的是 JDK
动态代理。
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
我们看一下最终生成的 Proxy
对象,在其他 Bean
中注入 UserFeign
的就是这个对象:
3.调用过程
代理对象如何生成的前面已经分析过了,接下来看一下调用过程。既然已经知道是 JDK
动态代理生成的了,那就直接跳转到方法调用:
// FeignInvocationHandler
static class FeignInvocationHandler implements InvocationHandler {
......
private final Map<Method, MethodHandler> dispatch;
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();
}
return dispatch.get(method).invoke(args);
}
}
equals
方法需要做一下判断,hashCode
和 toString
方法不做代理。最后一行用了一个 dispatch
对象来真正执行,那这个 dispatch
对象是什么呢?其实就是前面 newInstance
方法中的 methodToHandler
,这里就是从这个 Map
取出方法对应的 MethodHandler
,调用它的 invoke
方法。而前面已经分析过方法对应的 MethodHanlder
默认的类型是 SynchronousMethodHandler
。
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;
}
}
}
评论区