【OpenFeign】【源码+图解】【四】FeignClient实例工具类ReflectiveFeign
目录
- 6. 创建FeignClient接口的代理
- 6.1 收集方法的元数据
- 6.1.1 方法上基础信息
- 6.1.2 方法所在类的注解信息
- 6.1.3 方法上的注解信息
- 6.1.3.1 uri
- 6.1.3.2 produces
- 6.1.3.3 consumes
- 6.1.3.4 headers
- @RequestMapping
- @RequestHeader
- RequestInterceptor
- 6.1.4 参数上的注解信息
- 6.1.4.1 参数类型
- 6.1.4.2 参数上的Annotations
- 6.1.4.2.1 AnnotatedParameterContext
- 6.1.4.2.2 AnnotatedParameterProcessor
- 6.1.4.2.3 ConversionService&Param.Expander
- 6.1.4.3 URI
6. 创建FeignClient接口的代理
上一节讲到了ReflectiveFeign.newInstance()
public class ReflectiveFeign extends Feign {
@Override
public <T> T newInstance(Target<T> target) {
// 1、创建SynchronousMethodHandler(6.3节)
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()) {
// 过滤父类Object对象的方法
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
// 如果是default方法则绑定DefaultMethodHandler
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
// 2、其他的则将SynchronousMethodHandler与ProductFeignClient的方法绑定
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 3、创建FeignInvocationHandler(6.4节)
InvocationHandler handler = factory.create(target, methodToHandler);
// 4、创建ProductFeignClient的代理。
// 当调用ProductFeignClient的方法时由proxy代理,交给FeignInvocationHandler处理
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
}
创建SynchronousMethodHandler需要先收集FeignClient方法上的元信息以及创建RequestTemplate.Factory,所以接下来会分四步分析:
- 创建MethodMetadata,收集方法上的元数据
- 创建RequestTemplate.Factory
- 创建SynchronousMethodHandler
- 创建FeignInvocationHandler
6.1 收集方法的元数据
MethodMetadata是由Contract创建的,先看下其类图
它的作用是解析并检验FeignClient接口的方法,默认的Contract是SpringMvcContract,接下来就以它的parseAndValidateMetadata
方法开始
abstract class BaseContract implements Contract {
@Override
public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
// ......
final Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
for (final Method method : targetType.getMethods()) {
// ......
// 创建MethodMetadata,获取方法上的信息
final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
if (result.containsKey(metadata.configKey())) {
// 如果已经处理过,则覆盖原值并返回新类型
MethodMetadata existingMetadata = result.get(metadata.configKey());
Type existingReturnType = existingMetadata.returnType();
Type overridingReturnType = metadata.returnType();
Type resolvedType = Types.resolveReturnType(existingReturnType, overridingReturnType);
if (resolvedType.equals(overridingReturnType)) {
result.put(metadata.configKey(), metadata);
}
continue;
}
result.put(metadata.configKey(), metadata);
}
return new ArrayList<>(result.values());
}
}
接下来着重分析parseAndValidateMetadata(targetType, method)
,分四部分分析该方法:
- 基础信息
- 类级别的Annotation
- method上的Annotation
- 参数Parameters
流程图如下
6.1.1 方法上基础信息
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
// 1、记录处理过的method
processedMethods.put(Feign.configKey(targetType, method), method);
return super.parseAndValidateMetadata(targetType, method);
}
}
abstract class BaseContract implements Contract {
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
// 2、
final MethodMetadata data = new MethodMetadata();
data.targetType(targetType); // 接口类型
data.method(method); // 方法
data.returnType(
Types.resolve(targetType, targetType, method.getGenericReturnType())); // 返回类型
data.configKey(Feign.configKey(targetType, method)); // configKey
if (AlwaysEncodeBodyContract.class.isAssignableFrom(this.getClass())) {
data.alwaysEncodeBody(true); // 如果是AlwaysEncodeBodyContract类则设置此值,默认是SpringMvcContract
}
......
}
}
这里主要讲一下metadata.configKey()
举个例子,假设有下面的方法
@FeignClient("product")
public interface ProductFeignClient extends SecondInterface{
@GetMapping("/test")
public void configKey(String key, List<String> ids);
}
则它的configKey
为ProductFeignClient#configKey(String,List)),即类名+方法名+#+(参数类型))
6.1.2 方法所在类的注解信息
abstract class BaseContract implements Contract {
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
......
if (targetType.getInterfaces().length == 1) {
// 1、如果继承了其他Interface,先处理父类Interface
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
// 2、再处理自己的
processAnnotationOnClass(data, targetType);
......
}
}
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
// Interface上不能有@RequestMapping以及被它注解的,如@GetMapping等
if (classAnnotation != null) {
throw new IllegalArgumentException("@RequestMapping annotation not allowed on @FeignClient interfaces");
}
CollectionFormat collectionFormat = findMergedAnnotation(clz, CollectionFormat.class);
if (collectionFormat != null) {
// 如果Interface上有@CollectionFormat,则设置它的值,主要用来设置查询参数集合的分隔符号
data.template().collectionFormat(collectionFormat.value());
}
}
}
6.1.3 方法上的注解信息
abstract class BaseContract implements Contract {
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
......
for (final Annotation methodAnnotation : method.getAnnotations()) {
// 调用SpringMvcContract的processAnnotationOnMethod
processAnnotationOnMethod(data, methodAnnotation, method);
}
......
}
}
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
@Override
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
// 如果方法上有@CollectionFormat会覆盖类级别的
if (CollectionFormat.class.isInstance(methodAnnotation)) {
CollectionFormat collectionFormat = findMergedAnnotation(method, CollectionFormat.class);
data.template().collectionFormat(collectionFormat.value());
}
// 如果没有@RequestMapping等方法注解则忽略,直接返回
if (!RequestMapping.class.isInstance(methodAnnotation)
&& !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
return;
}
// 设置HttpMethod
RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
RequestMethod[] methods = methodMapping.method();
......
data.template().method(Request.HttpMethod.valueOf(methods[0].name()));
// 设置uri
if (methodMapping.value().length > 0) {
String pathValue = emptyToNull(methodMapping.value()[0]);
if (pathValue != null) {
// 将path中的${}变量替换成yml中的值
pathValue = resolve(pathValue);
// 见6.1.1.3.1
data.template().uri(pathValue, true);
if (data.template().decodeSlash() != decodeSlash) {
data.template().decodeSlash(decodeSlash);
}
}
}
// produces, 见6.1.1.3.2
parseProduces(data, method, methodMapping);
// consumes, 见6.1.1.3.3
parseConsumes(data, method, methodMapping);
// headers, 见6.1.1.3.4
parseHeaders(data, method, methodMapping);
data.indexToExpander(new LinkedHashMap<>());
}
}
6.1.3.1 uri
data.template().uri(pathValue, true)
具体代码不分析,看流程图
6.1.3.2 produces
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
private void parseProduces(MethodMetadata md, Method method, RequestMapping annotation) {
String[] serverProduces = annotation.produces();
// 只设置第一个
String clientAccepts = serverProduces.length == 0 ? null : emptyToNull(serverProduces[0]);
if (clientAccepts != null) {
// 设置Accept的值为clientAccepts
md.template().header(ACCEPT, clientAccepts);
}
}
}
6.1.3.3 consumes
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
private void parseConsumes(MethodMetadata md, Method method, RequestMapping annotation) {
String[] serverConsumes = annotation.consumes();
// 只设置第一个
String clientProduces = serverConsumes.length == 0 ? null : emptyToNull(serverConsumes[0]);
if (clientProduces != null) {
// 设置Content-Type的值为clientProduces
md.template().header(CONTENT_TYPE, clientProduces);
}
}
}
6.1.3.4 headers
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
private void parseHeaders(MethodMetadata md, Method method, RequestMapping annotation) {
if (annotation.headers() != null && annotation.headers().length > 0) {
// headers可设置多个
for (String header : annotation.headers()) {
int index = header.indexOf('=');
if (!header.contains("!=") && index >= 0) {
// ${}使用yml文件中的配置
md.template().header(resolve(header.substring(0, index)),
resolve(header.substring(index + 1).trim()));
}
}
}
}
}
对于headers的值可以通过三种方式获取:@RequestMapping
、@RequestHeader
、RequestInterceptor
@RequestMapping
可以从yml配置读取值
custom-headers: lanna
@FeignClient("product")
public interface ProductFeignClient extends SecondInterface{
@GetMapping(value = "/product", headers = { "author=${custom-headers}", "id=8001"})
public String getProduct(@RequestParam("ids") List<String> ids);
}
@RequestHeader
@FeignClient("product")
public interface ProductFeignClient extends SecondInterface{
@GetMapping(value = "/product")
public String getProduct(@RequestParam("ids") List<String> ids, @RequestHeader("author") String author);
}
RequestInterceptor
实现RequestInterceptor
@Configuration
public class MyRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("author", "lanna");
}
}
配置到FeignClient
public class MyConfiguration {
@Bean
MyRequestInterceptor requestInterceptor() {
return new MyRequestInterceptor();
}
}
// 配置MyRequestInterceptor
@FeignClient(value = "product", configuration = MyConfiguration.class)
public interface ProductFeignClient extends SecondInterface{
@GetMapping(value = "/product")
public String getProduct(@RequestParam("ids") List<String> ids);
}
对于produces和consumes的值也可采用相同的方法
6.1.4 参数上的注解信息
abstract class BaseContract implements Contract {
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
......
// 1、对于parameterTypes、genericParameterTypes的区别见6.1.1.4.1
final Class<?>[] parameterTypes = method.getParameterTypes();
final Type[] genericParameterTypes = method.getGenericParameterTypes();
final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
final int count = parameterAnnotations.length;
for (int i = 0; i < count; i++) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
// 2、processAnnotationsOnParameter
isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}
if (isHttpAnnotation) {
// 这里使用BitSet的形式保存ignoreParamater的配置
data.ignoreParamater(i);
}
if (parameterTypes[i] == URI.class) {
// 3、如果是URI类,则请求的路径则使用它
data.urlIndex(i);
} else if (!isHttpAnnotation
&& !Request.Options.class.isAssignableFrom(parameterTypes[i])) {
if (data.isAlreadyProcessed(i)) {
checkState(data.formParams().isEmpty() || data.bodyIndex() == null,
"Body parameters cannot be used with form parameters.%s", data.warnings());
} else if (!data.alwaysEncodeBody()) {
checkState(data.formParams().isEmpty(),
"Body parameters cannot be used with form parameters.%s", data.warnings());
checkState(data.bodyIndex() == null,
"Method has too many Body parameters: %s%s", method, data.warnings());
// 设置bodyIndex和bodyType,用于加密
data.bodyIndex(i);
data.bodyType(
Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}
}
......
return data;
}
}
6.1.4.1 参数类型
假设有一个方法如下
@FeignClient(value = "product")
public interface ProductFeignClient extends SecondInterface{
@GetMapping(value = "/product")
public String getProduct(@RequestParam("ids") List<String> ids, @RequestParam("status") Object status);
}
则parameterTypes、genericParameterTypes的区别如下,注意List
6.1.4.2 参数上的Annotations
在分析SpringMvcContract的processAnnotationsOnParameter
方法前必须先了解下面这4个类:
- AnnotatedParameterContext参数容器(见6.1.1.4.2.1)
- AnnotatedParameterProcessor参数处理器(见6.1.1.4.2.2)
- ConversionService参数类型转换器(见6.1.1.4.2.3)
- Param.Expander参数转换器(见6.1.1.4.2.3)
通过类图大体理清楚了SpringMvcContract与各个参数的关系,那么接下来就要弄清楚这些参数的赋值过程,SpringMvcContract是通过FeignClientsConfiguration文件注入的,分析下SpringMvcContract的构造函数
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
ConversionService conversionService, boolean decodeSlash) {
// AnnotatedParameterProcessor的7个实现类都会添加进来
List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors();
processors.addAll(annotatedParameterProcessors);
// 将processors转成Map,key为AnnotatedParameterProcessor.ANNOTATION,value为processor
annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
// DefaultFormattingConversionService
this.conversionService = conversionService;
convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
// feign.client.decodeSlash,默认true
this.decodeSlash = decodeSlash;
}
}
这样SpringMvcContract就创建好了,接下来先逐个介绍前面那4个类的功能
6.1.4.2.1 AnnotatedParameterContext
因为AnnotatedParameterContext的实现类只有一个,所以我们值分析SimpleAnnotatedParameterContext
private class SimpleAnnotatedParameterContext implements AnnotatedParameterProcessor.AnnotatedParameterContext {
private final MethodMetadata methodMetadata;
private final int parameterIndex;
// 1、 向methodMetadata添加参数名字
@Override
public void setParameterName(String name) {
nameParam(methodMetadata, name, parameterIndex);
}
// 2、向methodMetadata添加模板参数名字
@Override
public Collection<String> setTemplateParameter(String name, Collection<String> rest) {
return addTemplateParameter(rest, name);
}
}
6.1.4.2.2 AnnotatedParameterProcessor
其7个实现类的功能如下
6.1.4.2.3 ConversionService&Param.Expander
从前面的类图中可以看出ConversionService主要是作为ConvertingExpanderFactory的参数使用,所以我们只看ConvertingExpanderFactory
private static class ConvertingExpanderFactory {
private final ConversionService conversionService;
ConvertingExpanderFactory(ConversionService conversionService) {
this.conversionService = conversionService;
}
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
// 返回Param.Expander。该Param.Expander的作用就是利用conversionService将类型为typeDescriptor的value转换成STRING_TYPE_DESCRIPTOR
return value -> {
Object converted = conversionService.convert(value, typeDescriptor, STRING_TYPE_DESCRIPTOR);
return (String) converted;
};
}
}
经过前面的介绍就可以很容易分析SpringMvcContract的processAnnotationsOnParameter
方法了
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
boolean isHttpAnnotation = false;
// 1、创建参数容器
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data,
paramIndex);
// 获取方法
Method method = processedMethods.get(data.configKey());
// 遍历annotation
for (Annotation parameterAnnotation : annotations) {
// 2、annotation参数处理器
AnnotatedParameterProcessor processor = annotatedArgumentProcessors
.get(parameterAnnotation.annotationType());
// 处理器processor存在则处理annotation参数值
if (processor != null) {
Annotation processParameterAnnotation;
// 给@AliasFor的参数赋值
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation,
method, paramIndex);
// 设置isHttpAnnotation,如果是AnnotatedParameterProcessor七个实现类中的ANNOTATION之一,则isHttpAnnotation=true
isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
}
}
if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
// 3、创建类型描述符
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
// 4、如果能转化为String,则添加参数转换器
if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
// 5、创建转换为String的参数转换器
Param.Expander expander = convertingExpanderFactory.getExpander(typeDescriptor);
if (expander != null) {
// 设置该参数的转换器。将复杂类型转换成String
data.indexToExpander().put(paramIndex, expander);
}
}
}
return isHttpAnnotation;
}
}
6.1.4.3 URI
修改下Controller和FeignClient
@RestController
@RequestMapping("/feign")
public class ProductController {
@Autowired
private ProductFeignClient productFacade = null;
@GetMapping("/product")
public String getProduct() throws URISyntaxException {
// 赋值的URI必须是绝对路径
return productFacade.getProduct(null, new URI("http://product/product"));
}
}
@FeignClient(value = "product")
public interface ProductFeignClient extends SecondInterface{
// 为了测试URI我们把@GetMapping中的value值去掉
@GetMapping
public String getProduct(@RequestParam("ids") List<String> ids, URI uri);
}
重新启动后发起请求,依然能拿到结果。证明我们可以直接传参URI类型赋值请求URL。