OpenFeign源码解析
📹参考:
📒参考:作者整理的笔记
First
什么是Open Feign?
OpenFeign 是 Spring Cloud 全家桶的组件之一, 其核心的作用是为 Rest API 提供高效简洁的 RPC 调用方式
搭建测试项目
服务接口和实体
项目名称
cloud-feign-api
实体类
1 | public class Order implements Serializable { |
服务提供方
项目名称
cloud-feign-server
依赖 (pom.xml)
1 | <dependencies> |
配置文件(application.yml)
1 | server: |
配置类
无
启动类
1 | @SpringBootApplication |
控制器
1 | @RestController |
服务消费方
项目名称
cloud-feign-client
依赖 (pom.xml)
1 | <dependencies> |
配置文件(application.yml)
1 | server: |
配置类
1 | @Configuration |
启动类
1 | @SpringBootApplication |
控制器
1 | @RestController |
服务接口
1 | // http://localhost:9000/consumer/feign/order/get/1 |
问题:为何只定义接口而没有实现类?
思路分析
问题一:如何动态生成实现类做到?
动态代理 (cglib, jdk)
问题二:代理对象如何交给spring容器?
把Bean交给spring容器的方法:
1.xml 声明bean <bean id=””, class=””>
2.@ConponentScan + @Sevice/@Controller/@Repository/@Componet
3.@Import(XXX.class)
4.ImportSelector 接口 -> 返回类名数组
5.ImportBeanDefinitionRegistrar 接口 -> registerBeanDefinitions
6.@Bean 注解
7.FactoryBean 接口 -> getObject()
8.SingletonBeanRegistry.registerSingleton(); API
前五种方法bean的创建过程是交给spring负责的,流程如下
class -> bean definition -> bean -> put in cache
如何把一个第三方的对象(完全由程序员控制对象创建过程)交给Spring管理?
1.factoryBean
2.SingletonBeanRegistry.registerSingleton();
3.@Bean
openFeign源码采用的是factoryBean
问题三:多个接口需要写多个对应的factoryBean类吗?
不需要
1)只要定义一个factoryBean类,把接口的Class作为变量传给factoryBean
2)针对不同的接口需要创建不同的factoryBean对象,每个factoryBean对象所持有的接口类型是不同的。
1 | class FeignClientFactoryBean implements FactoryBean<Object> { |
关于FactoryBean:
问题四:一个factoryBean类如何创建多个持有不同的接口类型的对象?
不可以用原型模式。
关于原型模式:

1)创建多个Bean Definition
BeanDefinitionBuilder.build()
2)每个Bean Definition 指定不同的接口类型
BeanDefinitionBuilder.addPropertyValue(String name, @Nullable Object value)
BeanDefinitionBuilder.addConstructorArgValue(@Nullable Object value)
问题五:如何优雅地把自定义的Bean Definition交给Spring?
注意和问题二的区别:一个是代理对象,一个是bean定义
ImportBeanDefinitionRegistrar 接口
-> registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
@Import、ImportSelector、ImportBeanDefinitionRegistrar的使用和区别
1)@Import(XXX.class)一般配合ImportSelector或者ImportBeanDefinitionRegistrar使用
2)ImportSelector返回的是全类名数组,用于选择需要的配置类
3)ImportBeanDefinitionRegistrar提供BeanDefinitionRegistry,用于注册自定义的Bean Definition
关于@Import、ImportSelector:
问题六:如何获取带有@FeignClient注解的接口以及注解信息?
包扫描
Spring 提供ClassPathScanningCandidateComponentProvider类做包扫描功能
1 | public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware { |
源码解读
EnableFeignClients
1 |
|
FeignClientsRegistrar
1 | class FeignClientsRegistrar |
FeignClientFactoryBean
1 | class FeignClientFactoryBean |
DefaultTargeter
1 | class DefaultTargeter implements Targeter { |
Feign
1 | public abstract class Feign { |
ReflectiveFeign
1 | public class ReflectiveFeign extends Feign { |
总结
设计:只需要定义接口 + 注解, 没有具体的实现类
解决方案:根据接口动态生成代理对象,把增强功能封装在里面,并把此对象交给spring管理
技术点:动态代理,factoryBean接口,包扫描,如何把自定义的Bean 定义交给spring(ImportBeanDefinitionRegistrar), 如何把自定义的对象交给spring备份
本节OpenFeign的原理和mybatis原理几乎相同
Second
如何发送http请求?
如何组件化?
定义接口
1 | public interface Client { |
接口实现
发送http请求,是否存在已有的方案?
- rest template
- http client
- ok http
……
现有方案的很多组件(如response类等)和OpenFeign中定义接口中的组件不同
可以通过适配器模式整合现有方案
如何整合已有的方案?

1 | /** http client的适配器 */ |
适配器需要引入的包(Pom.xml)
1 | <dependency> |
如何动态选择实现方案?
插拔式:
提供几种思路:
1)JAVA SPI -> 无法提供依赖注入,无法动态地选择实现类
JAVA SPI调用类的无参构造方法进行实例化,而我们需要的是进行依赖注入
如果两种依赖都引入了,JAVA SPI会对两种都进行实例化,,而我们常常只需要一种,因此这并不是我们想要的
2)Dubbo SPI -> 额外添加dubbo依赖,Dubbo SPI 与其业务模型耦合
3)springboot的自动装配 -> open feign 作为spirngcloud组件之一直接依托于springboot
技巧:如何快速找到自动装配类?
1)Ctrl+G -> find Usages 功能 寻找new Instance
2)通过名字去猜 autoconfiguration结尾, 其中带有feign开头
3)直接通过 spring.factories 文件去搜索
spring-cloud-openfeign-core-2.2.1.RELEASE.jar -> META-INF/spring.factories
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
feign 的带负载均衡的自动配置类
1 |
|
HttpClient适配器的配置类
1 |
|
HttpClient的配置类
1 |
|
如果同时依赖了http client和ok http?
@import导入的时候按照顺序,先HttpClientFeignLoadBalancedConfiguration.class再OkHttpFeignLoadBalancedConfiguration.class。
而两个配置类都含有@ConditionalOnMissingBean(Client.class)
如果先导入HttpClientFeignLoadBalancedConfiguration后,就含有了Client的bean,再导入OkHttpFeignLoadBalancedConfiguration的时候就会条件不成立,不会导入。
1 | // 依照import的顺序 http client -> ok http -> jdk |
如何修改配置参数?
方法一:通过配置文件修改FeignHttpClientProperties的参数(属性绑定)
1 |
|
方法二:修改配置类(@Bean) 替换源码中的配置
自定义的配置类优先生效于框架的配置类
1 |
|
如何装配组件?
组件装配到哪里?
答案: SynchronousMethodHandler
1 | public class ReflectiveFeign extends Feign { |
1 | final class SynchronousMethodHandler implements MethodHandler { |
如何获取组件?
- Autowired 自动装配
- 通过aware接口获取BeanFactory或ApplicationContext,再从里面获取
OpenFeign采用第二种方式:
FeignClientFactoryBean类的getObject方法
→ loadBalance方法
1 | protected <T> T loadBalance(Feign.Builder builder, FeignClientFactory context, HardCodedTarget<T> target) { |
FeignClientFactory context是什么?
→ getOptional方法
1 | protected <T> T getOptional(FeignClientFactory context, Class<T> type) { |
→ getInstance方法
1 | public <T> T getInstance(String name, Class<T> type) { |
→ getContext方法(NamedContextFactory中)
1 | protected GenericApplicationContext getContext(String name) { |
其中_private final Map_<String, GenericApplicationContext> contexts;
对于每一个contextId,都会创建一个子容器。一个feign接口创建一个对应的子容器。配置好后存入mapcontexts
FeignContext继承了NamedContextFactory
上述过程如图:

getInstance方法得到的容器是子容器。**context.getBean(type);**的时候会先从子容器拿,拿不到再去父容器拿。(注意是getBean类型方式获得)
很多其他的组件也是这个思路。
为什么对每个feign接口都要创建一个子容器?
这样我们可以对每个feign接口进行单独的配置,每个接口的配置都可以不相同,接口对应的容器来解析每个接口的配置。例如,一个接口可以采用httpclient,而另一个接口可以采用okhttp。
最终这些所有的容器统一被放在FiegnContext中。
所以如何获取组件?就是从该接口对应的子容器中getBean类型得到组件。
如何传递组件?
1 | protected <T> T loadBalance(Feign.Builder builder, FeignClientFactory context, HardCodedTarget<T> target) { |
loadBalance中,把client组件传给了Feign.Builder builder
再通过 Feign.Builder 的build方法传给 SynchronousMethodHandler.Factory
最后通过SynchronousMethodHandler的create方法传给SynchronousMethodHandler
总结
设计:组件化思维
技术点:适配器模式,springboot自动装配(@Conditional注解的解读,@Import注解的顺序),父子容器
Third
配置体系
配置类
### **应用级别配置(全局)**
1
2
3
4
5
6
7
8
9
10
11
12
// 注册feign client的bean定义
public EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {}; // 默认配置全局有效
Class<?>[] clients() default {};
}
1
2
3
4
5
6
// 配置在启动类上
public class FeignClientMain {
// ...
}
### **服务级别配置**
1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
// ...
Class<?>[] configuration() default {}; // 只对服务接口有效
// ...
}
1
2
3
4
5
6
7
8
9
10
// 配置在服务接口
@FeignClient(value = "cloud-feign-server", contextId = "order", configuration = OrderConfiguration.class)
public interface OrderService {
// ...
}
@FeignClient(value = "cloud-feign-server", contextId = "user", configuration = UserConfiguration.class)
public interface UserService {
// ...
}
### **配置隔离原理**
一句话:通过spring子容器进行隔离,不同的feign client接口对应不同的子容器,里面有自己独立的配置
### **1) 注册配置类到spring父容器**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class **FeignClientsRegistrar //这个类通过@FeignCLients -> @Import注入**
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
/** ImportBeanDefinitionRegistrar的方法 */
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
/** 注册默认的配置类 */
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 获取ableFeignClients注解的信息
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
// 获取defaultConfiguration的值
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"));
}
}
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// ...
for (String basePackage : basePackages) {
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"));
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
// 注意这里不是注册配置类本身 注册的是FeignClientSpecification 但里面封装了配置类
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
// 注册FeignClientSpecification的bean定义
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(**FeignClientSpecification**.class);
builder.addConstructorArgValue(name);
// 把配置类通过构造方法传入
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
}
注意不是直接注册配置类本身,而是 FeignClientSpecification 类
1
2
3
4
5
6
7
8
9
public class FeignClientSpecification implements NamedContextFactory.Specification {
private String name;
private String className;
//配置类
private Class<?>[] configuration;
......
注册配置类本身和注册规格(FeignClientSpecification)类的区别:
如果直接注册配置类本身,就会走spring中refresh的流程,会把配置类中的bean都创建出来并放入父容器中
而注册规格类,在整个ioc期间就不会去解析配置类
### **2) 注入配置类到FeignContext**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class FeignAutoConfiguration {
// 把所有FeignClientSpecification对象注入到集合里面
**private List<FeignClientSpecification> configurations = new ArrayList<>();**
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
}
### **3) 从FeignContext中获取组件**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
// ...
// 使用配置类进行配置
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
// 从spring容器获取组件
Logger.Level level = getOptional(context, Logger.Level.class);
// ...
// 从spring容器获取组件
Map<String, RequestInterceptor> requestInterceptors = context
.**getInstances**(this.contextId, RequestInterceptor.class);
// ...
}
protected <T> T getOptional(FeignContext context, Class<T> type) {
return context.getInstance(this.contextId, type);
}
}
### **4) 创建子容器加载配置**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// FeignContext
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
// 传入FeignClients的官方默认配置类
super(FeignClient**s**Configuration.class, "feign", "feign.client.name");
}
}
// 带名字的上下文工厂
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
implements DisposableBean, ApplicationContextAware {
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType; // 传入官方默认配置类
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;
}
// 存储子容器的Map
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
// value是FeignClientSpecification对象
private Map<String, C> configurations = new ConcurrentHashMap<>();
// 父容器 通过ApplicationContextAware注入
private ApplicationContext parent;
// 默认配置类是FeignClientsConfiguration
private Class<?> defaultConfigType;
/** 把配置的List转为Map */
public void setConfigurations(List<C> configurations) {
for (C client : configurations) {
this.configurations.put(client.getName(), client);
}
}
// 从spring父子容器中获取单个对象
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
type).length > 0) {
return context.getBean(type);
}
return null;
}
// 从spring父子容器中获取多个对象
public <T> Map<String, T> getInstances(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
type).length > 0) {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}
return null;
}
/** 获取context */
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
/** 创建context */
protected AnnotationConfigApplicationContext createContext(String name) {
// 每个接口创建自己的子容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册属于服务接口的配置类
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
// 注册应用全局的配置类
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
// 注册默认的配置类
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
// 父容器就是当前应用的spring容器
if (this.parent != null) {
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
context.**refresh**();
return context;
}
}
### **配置类示意图**
parent context type : AnnotationConfigServletWebApplicationContext :不允许bean 定义覆盖
child context type: AnnotationConfigApplicationContext :允许bean 定义覆盖

子容器中有三种配置:全局默认配置,接口个性化配置,feignclient默认配置
### **问题:**
如果同时添加了全局和服务级别的配置,那会发生什么?
1)启动报错 2)全局配置生效 3)服务级别的配置生效
答案: 2)全局配置生效
两个bean的类型和名字都是一样的,会不会报错取决于一个属性allowBeanDefinitionOverriding:是否允许bean定义进行覆盖
父容器的allowBeanDefinitionOverriding是false,而子容器的allowBeanDefinitionOverriding是true
所以在子容器中允许bean定义的覆盖
在[**创建子容器加载配置**](/16e64052cea98131b9e3cd31d528bb7f)中,先加载的是接口个性化配置,后加载的是默认全局配置,所以全局配置会覆盖服务级别的配置
配置文件
application.properties 或 application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
feign:
client:
defaultToProperties: false
config: # 对应FeignClientProperties类的config成员变量
default: # 全局配置
# 日志级别
logger-level: BASIC
# 超时时间
connect-timeout: 10000
# 接口配置
order:
# 日志级别
logger-level: HEADERS
# 超时时间
connect-timeout: 8000
user:
# 日志级别
logger-level: FULL
# 超时时间
connect-timeout: 6000
属性绑定Properties类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 配置的前缀 feign.client
public class FeignClientProperties {
// 以配置文件的为准
private boolean defaultToProperties = true;
// 默认配置的名称 default
private String defaultConfig = "default";
// 可以自定义多个配置 key为配置名称
private Map<String, FeignClientConfiguration> config = new HashMap<>();
/**
* Feign client configuration.
*/
//注意不要和官方配置类(FeignClient**s**Configuration)混淆 这只是一个用来封装的普通类
public static class FeignClientConfiguration {
private Logger.Level loggerLevel; // 日志级别
private Integer connectTimeout; // 连接超时
private Integer readTimeout; // 读取超时
private Class<Retryer> retryer; // 重试
private Class<ErrorDecoder> errorDecoder; // 错误解码器
private List<Class<RequestInterceptor>> requestInterceptors; // 拦截器
private Boolean decode404;
private Class<Decoder> decoder; // 解码器
private Class<Encoder> encoder; // 编码器
private Class<Contract> contract; // 契约
}
}
### **配置类和配置文件的优先级**
由于`private String defaultConfig = "default";`,所以默认以配置文件为准
> ⚠️ 在使用配置文件配置的时候,先加载的是全局配置,后加载的是接口配置,所以服务级别的配置可以覆盖默认配置,与[**创建子容器加载配置**](/16e64052cea98131b9e3cd31d528bb7f)中相反
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
class FeignClientFactoryBean
implements FactoryBean < Object > , InitializingBean, ApplicationContextAware {
// ...
// 配置 feign
protected void configureFeign(FeignContext context, Feign.Builder builder) {
// 从配置文件获取(属性绑定)
FeignClientProperties properties = this.applicationContext
.getBean(FeignClientProperties.class);
if (properties != null) {
// 如果有配置文件有配置
if (properties.isDefaultToProperties()) {
// isDefaultToProperties默认为true 即默认以配置文件的配置为准
// 因此先通过配置类进行配置 然后通过配置文件进行配置
configureUsingConfiguration(context, builder);
// 对于配置文件而言 服务级别的配置可以覆盖默认配置
//加载全局配置
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
// 此contextId可以在@FeignClient注解属性中配置,也可以配置文件直接指定
//加载接口配置
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
} else {
// isDefaultToProperties如果设置为false 即默认以配置类的配置为准
// 因此先通过配置文件进行配置 然后通过配置类进行配置
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
// 此contextId可以在@FeignClient注解属性中配置,也可以配置文件直接指定
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
configureUsingConfiguration(context, builder);
}
} else {
// 如果配置文件没有配置则直接从配置类进行配置
configureUsingConfiguration(context, builder);
}
}
// 使用配置类进行配置
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
// 日志级别
Logger.Level level = getOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
}
// 重试器
Retryer retryer = getOptional(context, Retryer.class);
if (retryer != null) {
builder.retryer(retryer);
}
// 错误编码
ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
if (errorDecoder != null) {
builder.errorDecoder(errorDecoder);
}
// 请求参数(连接超时 读取超时等)
Request.Options options = getOptional(context, Request.Options.class);
if (options != null) {
builder.options(options);
}
// 拦截器
Map < String, RequestInterceptor > requestInterceptors = context
.getInstances(this.contextId, RequestInterceptor.class);
if (requestInterceptors != null) {
builder.requestInterceptors(requestInterceptors.values());
}
QueryMapEncoder queryMapEncoder = getOptional(context, QueryMapEncoder.class);
if (queryMapEncoder != null) {
builder.queryMapEncoder(queryMapEncoder);
}
if (this.decode404) {
builder.decode404();
}
}
// 使用配置文件进行配置
protected void configureUsingProperties(
FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
if (config == null) {
return;
}
// 日志级别
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
// 请求参数(连接超时 读取超时等)
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(),
config.getReadTimeout()));
}
// 重试器
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
// 错误编码
if (config.getErrorDecoder() != null) {
ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
builder.errorDecoder(errorDecoder);
}
// 拦截器
if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
for (Class < RequestInterceptor > bean: config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
builder.requestInterceptor(interceptor);
}
}
if (config.getDecode404() != null) {
if (config.getDecode404()) {
builder.decode404();
}
}
// 编码器
if (Objects.nonNull(config.getEncoder())) {
builder.encoder(getOrInstantiate(config.getEncoder()));
}
// 解码器
if (Objects.nonNull(config.getDecoder())) {
builder.decoder(getOrInstantiate(config.getDecoder()));
}
// 契约
if (Objects.nonNull(config.getContract())) {
builder.contract(getOrInstantiate(config.getContract()));
}
}
private < T > T getOrInstantiate(Class < T > tClass) {
try {
// 直接从spring父容器中取
return this.applicationContext.getBean(tClass);
} catch (NoSuchBeanDefinitionException e) {
return BeanUtils.instantiateClass(tClass);
}
}
// ...
}
具体配置举例讲解
### **请求拦截器**
接口:
1
2
3
public interface RequestInterceptor {
void apply(RequestTemplate template);
}
调用拦截器:发送请求前
作用:用于修改请求url, header, body等等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
final class SynchronousMethodHandler implements MethodHandler {
Request targetRequest(RequestTemplate template) {
// 调用拦截器
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
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 {
// 发送请求
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);
// ...
}
}
获取拦截器组件: 从配置类或配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
// 使用配置类进行配置
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
// ...
// 从spring容器获取组件
Map<String, RequestInterceptor> requestInterceptors = context
.getInstances(this.contextId, RequestInterceptor.class);
// ...
}
// 使用配置文件进行配置
protected void configureUsingProperties(
FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
// ...
// 拦截器
if (config.getRequestInterceptors() != null
&& !config.getRequestInterceptors().isEmpty()) {
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
builder.requestInterceptor(interceptor);
}
}
// ...
}
}
### **问题一:**
是否需要@Component注解?
可以。加入@Component注解后,拦截器会被加入父容器。因为getBean的时候是先先找子容器再一直向上追溯查找,所以会扫描到拦截器。
### **问题二:**
拦截器是全局有效的吗?如果是,可否做到只对某个服务接口有效?
可以做到。只需要在每个接口单独的配置类中配置该接口的拦截器,这样拦截器会被配置到每个接口的子容器中实现只对某个接口有效。
但是需要注意的一点是,要在@ComponentScan中排除掉接口配置类所在的包,以防止spring扫描到拦截器并将其加入到父容器中导致专一性失效。这种情况下配置隔离失效。
### **问题三:**
拦截器是否可以自定义顺序?
拦截器规定顺序的一些实现:提供拦截器order、提供registry类来配置拦截器等。
OpenFrign没有提供这一方面的实现。
但是我们可以通过代码中配置拦截器的代码顺序实现。因为配置类解析是有顺序的。
Fourth
请求对象的构造(上)
前三章节回顾
前三章的内容归纳起来就是讲了这样的问题:

如何把接口转换为具有发送http请求能力的feign client对象以及如何整合到Spring容器中?
如何构造请求对象?
### **思路分析**
### **Http请求对象的分析(目标)**
URL: [http://127.0.0.1:9000/consumer/feign/order/](http://127.0.0.1:9000/consumer/feign/order/){1}?name=xxx&age=18
协议: http
IP端口: 127.0.0.1:9000 -> 注册中心获取
URI: /consumer/feign/order/{id}
路径参: {1} (path variable)
请求参:name=xxx, age=18 (query)
请求头: headers
请求体: body
请求方法: Get/Post/Put/Delete ...
1
2
3
4
5
6
public final class Request {
private final HttpMethod httpMethod;
private final String url;
private final Map<String, Collection<String>> headers;
private final Body body;
}
### **接口方法的分析(数据源)**
方法本身的要素是否能表达所有Http请求的要素?
方法的要素:
方法名 ×
参数(名称与类型) √
返回值类型 ×
URI -> 注解 或 Java对象(URI对象)表示
请求方法 -> 注解
路径参、请求参、请求头、请求体 -> 方法的入参 + 注解
### **问题一:注解如何设计?**
1)URI 和 请求方法可以合并在一个注解中
2)对路径参、请求参、请求头、请求体分别设置对应的注解
### **feign:**
@RequestLine/@Param/@QueryMap/@HeaderMap/@Body
### **open feign:**
@RequestMapping/@PathVariable/@RequestParam/@SpringQueryMap/@RequestHeader/@RequestBody
URI: 类的@RequestMapping + 方法的@RequestMapping
请求方法: 方法的@RequestMapping
路径参:参数的@PathVariable
请求参:参数的@RequestParam + @SpringQueryMap
请求头: 类的@RequestMapping(produce/consume/header)
方法的@RequestMapping(produce/consume/header)
参数的@RequestHeader
### **问题二:为什么选择SpringMVC注解?**
SpringMVC: http 请求 -> Java 对象
open feign:Java 对象 -> http 请求
对于方法和注解信息,可以封装在新的对象中 -> 方法元数据
### **方法元数据的分析**
1. 各种参数的位置(索引)
2)参数名称,类型
3)参数类型转换器
4)编码信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class MethodMetadata implements Serializable {
private static final long serialVersionUID = 1L;
private String configKey;
private transient Type returnType;
private Integer urlIndex;
private Integer bodyIndex;
private Integer headerMapIndex;
private Integer queryMapIndex;
private boolean queryMapEncoded;
private transient Type bodyType;
private RequestTemplate template = new RequestTemplate();
private List<String> formParams = new ArrayList<String>();
private Map<Integer, Collection<String>> indexToName =
new LinkedHashMap<Integer, Collection<String>>();
private Map<Integer, Class<? extends Expander>> indexToExpanderClass =
new LinkedHashMap<Integer, Class<? extends Expander>>();
private Map<Integer, Boolean> indexToEncoded = new LinkedHashMap<Integer, Boolean>();
private transient Map<Integer, **Expander**> indexToExpander;
}
Expander为参数类型转换器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public Param {
String value() default "";
Class<? extends Expander> expander() default ToStringExpander.class;
/** @deprecated */
boolean encoded() default false;
public static final class ToStringExpander implements Expander {
public ToStringExpander() {
}
public String expand(Object value) {
return value.toString();
}
}
public interface Expander {
String expand(Object var1);
}
}
只适用于路径参数、请求参数、header,因为这三个都转为字符串。但是body不可以。
### **构造请求对象整体思路**

构建请求对象分两步走:
1)解析方法和注解(类、方法、参数),并把信息封装到方法元数据中 → 应用启动
2)结合方法元数据和实际参数,构建请求对象 → 方法调用
实参的类型转换,编码,填充
object[]是因为反射时invoke方法的参数。我们根据`MethodMetadata` 中的各种index数值,在数组中对应index的位置即可拿到请求参数的对象,构建request
### **问题三:如何转换成方法元数据?**
1)做成一个组件(Contract)
1
2
3
4
public interface Contract {
// 解析接口的注解信息并封装为方法元数据的集合
List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType);
}

模板方法的设计模式
接口 + 抽象实现 + 默认实现
接口:提供扩展性 -> Contract
抽象实现: 抽取公共逻辑 -> BaseContract
默认实现:提供基本功能的使用 -> Default(Feign中的实现), SpringMvcContract(OpenFeign中的实现,因为其未使用Feign中的那一套注解)
2)Contract组件从何获得?
Springboot自动装配 + 从FeignContext获取
1
2
3
4
5
6
7
8
9
public class FeignClientsConfiguration {
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
}
### **源码解读**
### **BaseContract**
解析注解的顺序:类 -> 方法 -> 参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
abstract class BaseContract implements Contract {
/** 解析接口的注解信息并封装为方法元数据的集合 */
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
// 接口不能带有泛型
checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s",
targetType.getSimpleName());
// 接口最多只能有一个父接口
checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s",
targetType.getSimpleName());
// 如果传入的接口有一个父接口 那么该父接口必须是顶级接口
if (targetType.getInterfaces().length == 1) {
checkState(targetType.getInterfaces()[0].getInterfaces().length == 0,
"Only single-level inheritance supported: %s",
targetType.getSimpleName());
}
// 新建一个结果集容器
Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
// 获取所有public方法,包括从父接口继承而来的
for (Method method : targetType.getMethods()) {
// 排除掉从Object继承的方法,static方法,接口中的default方法
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
// 把方法解析为方法元数据 【关键代码】
MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
// 重写方法不支持
checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s",
metadata.configKey());
result.put(metadata.configKey(), metadata);
}
return new ArrayList<>(result.values());
}
/** 解析方法的注解并封装为方法元数据对象 */
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
// 创建MethodMetadata对象
MethodMetadata data = new MethodMetadata();
// 设置返回值
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
// 设置configKey,方法的唯一标识: 接口名#方法名(参数类型名称1,参数类型名称2)
data.configKey(Feign.configKey(targetType, method));
// 如果有父接口先处理父接口
if (targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
// 再处理当前接口 【关键代码】
processAnnotationOnClass(data, targetType);
// 处理方法的注解 【关键代码】
for (Annotation methodAnnotation : method.getAnnotations()) {
processAnnotationOnMethod(data, methodAnnotation, method);
}
// 只支持GET POST等http方法
checkState(data.template().method() != null,
"Method %s not annotated with HTTP method type (ex. GET, POST)",
method.getName());
// 获取参数原始类型
Class<?>[] parameterTypes = method.getParameterTypes();
// 获取参数通用类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
// 获取参数注解 二维数组:因为可以有多个参数 每个参数有多个注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int count = parameterAnnotations.length;
for (int i = 0; i < count; i++) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
// 处理每个参数的注解 如果其中有一个注解属于http注解 则isHttpAnnotation为true
// 哪些属于http注解?如SpringMVC的@RequestHeader @PathVariable @RequestParam @SpringQueryMap
//【关键代码】
isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}
if (parameterTypes[i] == URI.class) {
data.urlIndex(i);
} else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
// 参数类型不是URI或Options 也没有加http注解 则该参数判定为body
checkState(data.formParams().isEmpty(),
"Body parameters cannot be used with form parameters.");
checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method);
// 设置body的位置和类型【关键代码】
data.bodyIndex(i);
data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}
// ...
return data;
}
/** 处理类上的注解 */
protected abstract void processAnnotationOnClass(MethodMetadata data, Class<?> clz);
/** 处理方法上的注解 */
protected abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method);
/** 处理参数上的注解 */
protected abstract boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex);
}
> ⚠️ 参数类型不是URI或Options 也没有加http注解 则该参数判定为body → 不加@RequestBody也会被认定为body
http注解有:@PathVariable @SpringQueryMap @RequestHeader @RequestParam
### **SpringMvcContract**
类:@RequestMapping
方法:@RequestMapping
参数:@PathVariable @SpringQueryMap @RequestHeader @RequestParam
@RequestMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public RequestMapping {
String[] value() default {};
String[] path() default {};
/**
* The HTTP request methods to map to, narrowing the primary mapping:
* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
*/
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
/**
* header的Content-Type
*/
String[] consumes() default {};
/**
* header的Accept
*/
String[] produces() default {};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
private static final String ACCEPT = "Accept";
private static final String CONTENT_TYPE = "Content-Type";
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor
.valueOf(String.class);
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor
.valueOf(Iterable.class);
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
// 参数处理器 可以自动装配也可以使用默认的处理器
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
private final Map<String, Method> processedMethods = new HashMap<>();
private final ConversionService conversionService;
private final ConvertingExpanderFactory convertingExpanderFactory;
private ResourceLoader resourceLoader = new DefaultResourceLoader();
public SpringMvcContract(
List<AnnotatedParameterProcessor> annotatedParameterProcessors,
ConversionService conversionService) {
Assert.notNull(annotatedParameterProcessors,
"Parameter processors can not be null.");
Assert.notNull(conversionService, "ConversionService can not be null.");
// 初始化参数处理器
List<AnnotatedParameterProcessor> processors;
if (!annotatedParameterProcessors.isEmpty()) {
processors = new ArrayList<>(annotatedParameterProcessors);
}
else {
processors = getDefaultAnnotatedArgumentsProcessors();
}
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
// 创建参数转换器工厂 真正的转换功能来自conversionService
this.conversionService = conversionService;
this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
}
/** 获取默认处理器 */
private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() {
List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();
annotatedArgumentResolvers.add(new PathVariableParameterProcessor()); // 处理@PathVavirable
annotatedArgumentResolvers.add(new RequestParamParameterProcessor()); // 处理@RequestParam
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor()); // 处理@RequestHeader
annotatedArgumentResolvers.add(new QueryMapParameterProcessor()); // 处理@SpringQueryMap
return annotatedArgumentResolvers;
}
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
// 方法先放入缓存中 表示已经处理
this.processedMethods.put(Feign.configKey(targetType, method), method);
// 调用父类的parseAndValidateMetadata
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
// 处理类上的RequestMapping注解
// 因为RequestMapping注解可以加在类上和方法上 两者中注解值有优先级问题
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// 解析header中的produces
// 此时可能已经从方法的RequestMapping注解获得produces的值
// 这样处理表示方法上的RequestMapping注解优先于类上的RequestMapping注解
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// 解析header中的consumes 原理同produces
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// 解析headers
parseHeaders(md, method, classAnnotation);
}
return md;
}
/** 处理类上的注解(RequestMapping) */
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) {
RequestMapping classAnnotation = findMergedAnnotation(clz,
RequestMapping.class);
// 这里只处理类上RequestMapping的path,
// 其他produces, consumes, headers放在解析方法上的RequestMapping注解之后
if (classAnnotation != null) {
// 如果类上的@RequestMapping有value(path) 处理后放入uri中
if (classAnnotation.value().length > 0) {
String pathValue = emptyToNull(classAnnotation.value()[0]);
// 解析path中的${}
pathValue = resolve(pathValue);
// 保证uri以/开头
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
// 放入uri中
data.template().uri(pathValue);
}
}
}
}
/** 处理方法上的注解(RequestMapping) */
protected void processAnnotationOnMethod(MethodMetadata data,
Annotation methodAnnotation, Method method) {
// 如果不是@RequestMapping注解本身 也不带有@RequestMapping注解的话就返回
if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
.annotationType().isAnnotationPresent(RequestMapping.class)) {
return;
}
RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
// 解析HTTP Method
RequestMethod[] methods = methodMapping.method();
if (methods.length == 0) {
methods = new RequestMethod[] { RequestMethod.GET };
}
checkOne(method, methods, "method");
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);
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().uri(pathValue, true);
}
}
// 解析header中的produces
parseProduces(data, method, methodMapping);
// 解析header中的consumes
parseConsumes(data, method, methodMapping);
// 解析headers
parseHeaders(data, method, methodMapping);
data.indexToExpander(new LinkedHashMap<Integer, Param.Expander>());
}
/** 处理参数上的注解 */
protected boolean processAnnotationsOnParameter(MethodMetadata data,
Annotation[] annotations, int paramIndex) {
boolean isHttpAnnotation = false;
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
data, paramIndex);
Method method = this.processedMethods.get(data.configKey());
for (Annotation parameterAnnotation : annotations) {
// 根据参数注解类型获取对应的参数处理器
AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors
.get(parameterAnnotation.annotationType());
if (processor != null) {
Annotation processParameterAnnotation;
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
parameterAnnotation, method, paramIndex);
// 参数处理器处理【关键代码】
isHttpAnnotation |= processor.processArgument(context,
processParameterAnnotation, method);
}
}
// 如果是http注解并且没有对应的expander
// 什么expander -> 参数转换器
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
if (this.conversionService.canConvert(typeDescriptor,
STRING_TYPE_DESCRIPTOR)) {
Param.Expander expander = this.convertingExpanderFactory
.getExpander(typeDescriptor);
if (expander != null) {
data.indexToExpander().put(paramIndex, expander);
}
}
}
return isHttpAnnotation;
}
// ...
}
### **AnnotatedParameterProcessor**
PathVariableParameterProcessor:@PathVariable 解析路径参数
QueryMapParameterProcessor: @SpringQueryMap 解析请求参数
RequestHeaderParameterProcessor: @RequestHeader 解析请求头
RequestParamParameterProcessor:@RequestParam 解析请求参数
QueryMapParameterProcessor 与 RequestParamParameterProcessor的区别:
前者可以解析自定义实体对象,Map和基本类型,没有特别的限制
后者只能解析Map和基本类型不能解析自定义对象类型
### **QueryMapParameterProcessor**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<SpringQueryMap> ANNOTATION = SpringQueryMap.class;
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int paramIndex = context.getParameterIndex();
MethodMetadata metadata = context.getMethodMetadata();
// 对@SpringQueryMap注解所对应的参数的类型没有限制
if (metadata.queryMapIndex() == null) {
metadata.queryMapIndex(paramIndex);
metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded());
}
return true;
}
}
### **RequestParamParameterProcessor**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class RequestParamParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<RequestParam> ANNOTATION = RequestParam.class;
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
// 参数必须是Map类型 否则不可以成为QueryMap
if (Map.class.isAssignableFrom(parameterType)) {
checkState(data.queryMapIndex() == null,
"Query map can only be present once.");
data.queryMapIndex(parameterIndex);
return true;
}
RequestParam requestParam = ANNOTATION.cast(annotation);
String name = requestParam.value();
checkState(emptyToNull(name) != null,
"RequestParam.value() was empty on parameter %s", parameterIndex);
context.setParameterName(name);
Collection<String> query = context.setTemplateParameter(name,
data.template().queries().get(name));
data.template().query(name, query);
return true;
}
}
实参类型转换和填充
1
2
3
4
5
6
7
interface Expander {
/**
* Expands the value into a string. Does not accept or return null.
*/
String expand(Object value);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor
.valueOf(String.class);
private static class ConvertingExpanderFactory {
private final ConversionService conversionService;
ConvertingExpanderFactory(ConversionService conversionService) {
this.conversionService = conversionService;
}
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
return value -> {
Object converted = this.conversionService.convert(value, typeDescriptor,
STRING_TYPE_DESCRIPTOR);
return (String) converted;
};
}
}
}
Java 中的所有类型
raw type:原始类型,对应 Class
即我们通常说的引用类型,包括普通的类,例如 String.class、List.class
也包括数组(Array.class)、接口(Cloneable.class)、注解(Annotation.class)、枚举(Enum.class)等
primitive types:基本类型,对应 Class
包括 Built-in 内置类型,例如 int.class、char.class、void.class
也包括 Wrappers 内置类型包装类型,例如 Integer.class、Boolean.class、Void.class
parameterized types:参数化类型,对应 ParameterizedType
带有类型参数的类型,即常说的泛型,例如 List<T>、Map<Integer, String>、List<? extends Number>
实现类 sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
type variables:类型变量类型,对应 TypeVariable<D>
即参数化类型 ParameterizedType 中的 E、K 等类型变量,表示泛指任何类
实现类 sun.reflect.generics.reflectiveObjects.TypeVariableImpl
array types:泛型数组类型,对应 GenericArrayType
元素类型是参数化类型或者类型变量的泛型数组类型,例如 T[]
实现类 sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
Type 接口的另一个子接口 WildcardType 代表通配符表达式类型,或泛型表达式类型,比如?、? super T、? extends T,他并不是 Java 类型中的一种。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {
private final QueryMapEncoder queryMapEncoder;
protected final MethodMetadata metadata;
private final Map<Integer, Expander> indexToExpander = new LinkedHashMap<Integer, Expander>();
/** 通过metadata信息和实参创建RequestTemplate */
@Override
public RequestTemplate create(Object[] argv) {
// 把metadata中的半成品template拷贝一份
RequestTemplate mutable = RequestTemplate.from(metadata.template());
// 处理URI对象
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.target(String.valueOf(argv[urlIndex]));
}
//
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
RequestTemplate template = resolve(argv, mutable, varBuilder);
// 处理queryMap
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
// 处理headerMap
if (metadata.headerMapIndex() != null) {
template =
addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}
return template;
}
@SuppressWarnings("unchecked")
private RequestTemplate addHeaderMapHeaders(Map<String, Object> headerMap,
RequestTemplate mutable) {
for (Entry<String, Object> currEntry : headerMap.entrySet()) {
Collection<String> values = new ArrayList<String>();
Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null : nextObject.toString());
}
} else {
values.add(currValue == null ? null : currValue.toString());
}
mutable.header(currEntry.getKey(), values);
}
return mutable;
}
@SuppressWarnings("unchecked")
private RequestTemplate addQueryMapQueryParameters(Map<String, Object> queryMap,
RequestTemplate mutable) {
for (Entry<String, Object> currEntry : queryMap.entrySet()) {
Collection<String> values = new ArrayList<String>();
boolean encoded = metadata.queryMapEncoded();
Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null
: encoded ? nextObject.toString()
: UriUtils.encode(nextObject.toString()));
}
} else {
values.add(currValue == null ? null
: encoded ? currValue.toString() : UriUtils.encode(currValue.toString()));
}
mutable.query(encoded ? currEntry.getKey() : UriUtils.encode(currEntry.getKey()), values);
}
return mutable;
}
// ...
}