交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
Fegin 第5 章:Contract
分享
未结
0
1112
李延
LV6
2021-06-22
悬赏:20积分
# 作用 通过class对象,将其方法上的注解、参数、返回值等信息封装为MethodMetadata对象,进行返回。 # 初始化 当我们使用springcloud时,在中我们看到了它的初始化 ```java @Bean @ConditionalOnMissingBean public Contract feignContract(ConversionService feignConversionService) { boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash(); return new SpringMvcContract(this.parameterProcessors, feignConversionService, decodeSlash); } ``` 其中初始化的是SpringMvcContract,其中需要两个参数 - ConversionService 这个是spring核心类,用于数据类型的转换,不再重复说明 - List<AnnotatedParameterProcessor> parameterProcessors 我们看到通过spring注入了多个AnnotatedParameterProcessor 这个是参数处理器,我们在后续说明 # 继承关系  # Contract 接口,只定义了一个方法 ```java List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType); ``` 通过class类型,解析出这个类里所有的method信息。 # BaseContract 实现 parseAndValidateMetadata方法 ```java @Override public List<MethodMetadata> parseAndValidateMetadata(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()); } //创建list final Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>(); // 循环所有方法 for (final Method method : targetType.getMethods()) { // 对于Object的方法和默认方法跳过 if (method.getDeclaringClass() == Object.class || (method.getModifiers() & Modifier.STATIC) != 0 || Util.isDefault(method)) { continue; } // 根据method解析出MethodMetadata对象 final 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()); } ``` 这个方法我们看到它循环了class里每个方法,通过parseAndValidateMetadata 对每个method解析出MethodMetadata ## parseAndValidateMetadata ```java protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) { //创建MethodMetadata对象 final MethodMetadata data = new MethodMetadata(); //设置class与method data.targetType(targetType); data.method(method); //设置返回值 data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); //设置key 根据类名+方法名+参数名 data.configKey(Feign.configKey(targetType, method)); //如果有父接口,只处理第一个 if (targetType.getInterfaces().length == 1) { processAnnotationOnClass(data, targetType.getInterfaces()[0]); } //处理class上的注解 processAnnotationOnClass(data, targetType); //处理method上的注解 for (final Annotation methodAnnotation : method.getAnnotations()) { processAnnotationOnMethod(data, methodAnnotation, method); } if (data.isIgnored()) { return data; } checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)%s", data.configKey(), data.warnings()); //获取所有参数 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) { isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i); } //记录已经处理的参数编号 if (isHttpAnnotation) { data.ignoreParamater(i); } // 如果是url类型记录urlindex if (parameterTypes[i] == URI.class) { data.urlIndex(i); // 如果是忽略类型,将其设置为bodyIndex } else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) { if (data.isAlreadyProcessed(i)) { checkState(data.formParams().isEmpty() || data.bodyIndex() == null, "Body parameters cannot be used with form parameters.%s", data.warnings()); } else { 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()); data.bodyIndex(i); data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i])); } } } //数据校验 if (data.headerMapIndex() != null) { checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]); } if (data.queryMapIndex() != null) { if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) { checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]); } } return data; } ``` 这里我们看到主要分为一下几步 - 处理返回值。 - 处理class上的逐渐。 - 处理method上的注解。 - 处理请求参数。 其中 返回值通过Types类进行处理,我们单独说明 class、method、参数等都由子类属性,我们在具体的子类中讲解。 对于参数有2个特殊情况 1. url类型的数据,会被记录为urlindex 2. 没有被解析的数据会被记录为bodyindex 其具体作用我们在使用中说明。 # SpringMvcContract 通过前面的分析我们知道这个类主要实现了class、method、参数的解析过程。我们重点关注 ## 构造方法 ```java public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService) { this(annotatedParameterProcessors, conversionService, true); } public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService, boolean decodeSlash) { Assert.notNull(annotatedParameterProcessors, "Parameter processors can not be null."); Assert.notNull(conversionService, "ConversionService can not be null."); //获取默认的AnnotatedParameterProcessor,用于解析参数 List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors(); processors.addAll(annotatedParameterProcessors); //添加参数传入的数据 annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors); //设置conversionService this.conversionService = conversionService; convertingExpanderFactory = new ConvertingExpanderFactory(conversionService); this.decodeSlash = decodeSlash; } ``` 这里我们看到主要是对AnnotatedParameterProcessor的设置,处理最开始我们通过spring 传入的对象外,还有getDefaultAnnotatedArgumentsProcessors设置的默认对象。 ```java private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() { List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>(); annotatedArgumentResolvers.add(new MatrixVariableParameterProcessor()); annotatedArgumentResolvers.add(new PathVariableParameterProcessor()); annotatedArgumentResolvers.add(new RequestParamParameterProcessor()); annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor()); annotatedArgumentResolvers.add(new QueryMapParameterProcessor()); annotatedArgumentResolvers.add(new RequestPartParameterProcessor()); return annotatedArgumentResolvers; } ``` 这里我们看到了有6个解析器,我们在后续单独说明 ## processAnnotationOnClass 解析class上的注解 ```java @Override protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) { if (clz.getInterfaces().length == 0) { RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class); if (classAnnotation != null) { // Prepend path from class annotation if specified if (classAnnotation.value().length > 0) { String pathValue = emptyToNull(classAnnotation.value()[0]); pathValue = resolve(pathValue); if (!pathValue.startsWith("/")) { pathValue = "/" + pathValue; } data.template().uri(pathValue); if (data.template().decodeSlash() != decodeSlash) { data.template().decodeSlash(decodeSlash); } } } } } ``` 代码比较简单,就是处理RequestMapping注解,获取url将其设置到data.template().uri上 ## processAnnotationOnMethod 解析方法上的注解 ```java @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()); } if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) { return; } //解析RequestMapping RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class); // HTTP Method RequestMethod[] methods = methodMapping.method(); //默认get请求 if (methods.length == 0) { methods = new RequestMethod[] { RequestMethod.GET }; } checkOne(method, methods, "method"); //设置请求方式 data.template().method(Request.HttpMethod.valueOf(methods[0].name())); // path //设置请求路径和class上的设置进行拼装 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); } } } // produces head上的ACCEPT设置 parseProduces(data, method, methodMapping); // consumes head中 CONTENT_TYPE设置 parseConsumes(data, method, methodMapping); // headers 处理headers parseHeaders(data, method, methodMapping); data.indexToExpander(new LinkedHashMap<>()); } ``` 这个方法主要是对RequestMapping解析,包括请求路径、请求方式、以及header的一些设置 同时包含CollectionFormat注解。我们在使用时说明 ## processAnnotationsOnParameter 参数的解析 ```java @Override protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) { boolean isHttpAnnotation = false; AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data, paramIndex); Method method = processedMethods.get(data.configKey()); for (Annotation parameterAnnotation : annotations) { AnnotatedParameterProcessor processor = annotatedArgumentProcessors .get(parameterAnnotation.annotationType()); if (processor != null) { Annotation processParameterAnnotation; // synthesize, handling @AliasFor, while falling back to parameter name on // missing String #value(): processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation, method, paramIndex); isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method); } } if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) { TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex); if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) { Param.Expander expander = convertingExpanderFactory.getExpander(typeDescriptor); if (expander != null) { data.indexToExpander().put(paramIndex, expander); } } } return isHttpAnnotation; } ``` 对于参数的解析都是交给AnnotatedParameterProcessor来处理的,其中我们在具体章节中说明
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号