17、Contract 创建方法元数据(MethodMetadata)源码分析

前言

在之前,我们了解到Feign 基于接口声明,生成执行请求的动态代理对象,那么究竟是如何将接口上的注解等信息,解析的呢?这里就必须了解Feign 中非常重要的一个接口Contract

Contract意为契约,实际就是Feign 中的接口解析器,会解析接口方法上的注解为元数据,以此来创建请求模板及方法执行器。

MethodMetadata

MethodMetadata意为方法元数据,可以理解为一个Java Bean,Feign 会解析接口的每个方法,封装为MethodMetadata,并依据这个创建方法处理器。

    // 键名=》OrderFeign#post(Order)
    private String configKey;
    // 方法返回类型=》Order
    private transient Type returnType;
    // 请求url位置
    private Integer urlIndex;
    // 请求体位置
    private Integer bodyIndex;
    // 消息头位置
    private Integer headerMapIndex;
    // queryMap位置
    private Integer queryMapIndex;
    private boolean queryMapEncoded;
    // 请求体类型
    private transient Type bodyType;
    // 请求模板
    private final RequestTemplate template = new RequestTemplate();
    private final List<String> formParams = new ArrayList();
    private final Map<Integer, Collection<String>> indexToName = new LinkedHashMap();
    private final Map<Integer, Class<? extends Expander>> indexToExpanderClass = new LinkedHashMap();
    private final Map<Integer, Boolean> indexToEncoded = new LinkedHashMap();
    private transient Map<Integer, Expander> indexToExpander;
    private BitSet parameterToIgnore = new BitSet();
    // 是否忽略该方法
    private boolean ignored;
    // 需要代理的接口类型
    private transient Class<?> targetType;
    // 接口中的方法 =》public abstract pojo.Order account.OrderFeign.post(pojo.Order)
    private transient Method method;
    // 警告级别
    private final transient List<String> warnings = new ArrayList();

Contract 接口及其实现类

Contract 接口只有一个方法,parseAndValidateMetadata (解析并校验元数据),转入一个Class类型,将其解析为MethodMetadata

public interface Contract {

    List<MethodMetadata> parseAndValidateMetadata(Class<?> var1);
 }

BaseContract是Contract 的抽象类,实现了其parseAndValidateMetadata 方法,它的解析方法中,会解析接口上的所有方法(包含父类接口)为MethodMetadata,但不包含Object、静态、默认等方法。

SpringMvcContract 提供了基于Spring MVC注解的解析,可以解析@RequestMapping注解,当然衍生的@GetMapping也是可以的。
&nbsp;

加载流程源码解析

1. 进入动态代理

在项目启动,创建动态代理对象时,会调用Contract进行方法解析,这里可以看到这里为SpringMvcContract ,也可以看到SpringMvcContract中有很多解析器,可以解析很多Spring MVC的注解。
&nbsp;
注解参数处理器AnnotatedParameterProcessor有如下实现类,分别对应了解析@MatrixVariable、@PathVariable、@SpringQueryMap、@RequestHeader、@RequestParam、@RequestPart等Spring MVC 提供的注解。
&nbsp;

2. 进入解析

接着调用到SpringMvcContract的arseAndValidateMetadata(Class<?> targetType)方法,会查询当前接口及父接口中的方法,并循环这些方法,依次处理解析,最终放入到Map中(eg: {" OrderFeign#post(Order)","MethodMetadata"}),

        public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {

            // 检查泛型,说明Feign 接口,不能添加泛型
            Util.checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", new Object[]{

     targetType.getSimpleName()});
            // 检查继承的接口,说明最多只能有一个父接口
            Util.checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", new Object[]{

     targetType.getSimpleName()});
            if (targetType.getInterfaces().length == 1) {

                // 有父接口时,再检查是父接口是否有父接口,说明父接口必须是顶级接口
                Util.checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", new Object[]{

     targetType.getSimpleName()});
            }

            Map<String, MethodMetadata> result = new LinkedHashMap();
            // 获取接口上的所有方法
            Method[] var3 = targetType.getMethods();
            int var4 = var3.length;
            // 循环方法
            for(int var5 = 0; var5 < var4; ++var5) {

                Method method = var3[var5];
                // 不是Object 上的方法、不是static、不是default 方法(接口),则会进入解析
                if (method.getDeclaringClass() != Object.class && (method.getModifiers() & 8) == 0 && !Util.isDefault(method)) {

                    // 调用parseAndValidateMetadata解析
                    MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method);
                    Util.checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s", new Object[]{

     metadata.configKey()});
                    // 放入到Map 中 OrderFeign#post(Order)=》
                    result.put(metadata.configKey(), metadata);
                }
            }

            return new ArrayList(result.values());
        }

依次处理解析进入到parseAndValidateMetadata(Class<?> targetType, Method method)方法,该方法会对 @RequestMapping 注解中的配置属性进行解析,比如produces、consumes等。

    public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {

        // 已处理的方法
        this.processedMethods.put(Feign.configKey(targetType, method), method);
        // 调用parseAndValidateMetadata
        MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
        // 查询 RequestMapping 注解,并解析属性。
        RequestMapping classAnnotation = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(targetType, RequestMapping.class);
        if (classAnnotation != null) {

            if (!md.template().headers().containsKey("Accept")) {

                this.parseProduces(md, method, classAnnotation);
            }

            if (!md.template().headers().containsKey("Content-Type")) {

                this.parseConsumes(md, method, classAnnotation);
            }

            this.parseHeaders(md, method, classAnnotation);
        }

        return md;
    }

在对@RequestMapping 注解进行属性解析之前,会进入到parseAndValidateMetadata(Class<?> targetType, Method method)方法进行注解解析,这个是主要的解析方法,会完成方法上注解、形参注解的解析:

        protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {

            MethodMetadata data = new MethodMetadata();
            data.targetType(targetType); // 接口类型
            data.method(method); // 方法 public abstract java.util.List account.OrderFeign.insertOrder(java.lang.Long,java.lang.String,java.lang.Long,java.lang.Long)
            // 方法返回类型
            data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
            // 生产OrderFeign#insertOrder(Long,String,Long,Long)
            data.configKey(Feign.configKey(targetType, method));
            if (targetType.getInterfaces().length == 1) {

                // 获取父类中的注解并处理
                this.processAnnotationOnClass(data, targetType.getInterfaces()[0]);
            }
            // 处理@RequestMapping 注解
            this.processAnnotationOnClass(data, targetType);
            // 获取方法上的所有注解,这里是@GetMapping
            Annotation[] var4 = method.getAnnotations();
            int var5 = var4.length;

            for(int var6 = 0; var6 < var5; ++var6) {

                Annotation methodAnnotation = var4[var6];
                // 处理Spring MVC 注解,RequestMapping、GetMapping等,将请求方式,路径设置到模板中,
                // 注解中的headers、consumes、headers 都会被添加到请求模板中
                // 因为 GetMapping也是RequestMapping,所以这里会被解析到
                this.processAnnotationOnMethod(data, methodAnnotation, method);
            }
            // 配置了忽略,则直接跳过
            if (data.isIgnored()) {

                return data;
            } else {

                // 检查请求方式
                Util.checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)%s", new Object[]{

     data.configKey(), data.warnings()});
                // 所有参数的类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                Type[] genericParameterTypes = method.getGenericParameterTypes();
                // 获取参数注解
                Annotation[][] parameterAnnotations = method.getParameterAnnotations();
                // 参数的长度
                int count = parameterAnnotations.length;

                for(int i = 0; i < count; ++i) {

                    // 是否配置了Spring MVC 的参数注解,比如@RequestParam
                    boolean isHttpAnnotation = false;
                    if (parameterAnnotations[i] != null) {

                        // 处理形参,添加到请求模板中。
                        isHttpAnnotation = this.processAnnotationsOnParameter(data, parameterAnnotations[i], i);
                    }
                    // 设置该序号的参数也被处理
                    if (isHttpAnnotation) {

                        data.ignoreParamater(i);
                    }
                    // 校验参数是否是 URI类型
                    if (parameterTypes[i] == URI.class) {

                        data.urlIndex(i);
                    } else if (!isHttpAnnotation && parameterTypes[i] != Options.class) {

            // 如果不是 RequestParam\Map 类型的参数,则会解析为请求体。
                        if (!data.isAlreadyProcessed(i)) {

                            Util.checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters.%s", new Object[]{

     data.warnings()});
                            Util.checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s%s", new Object[]{

     method, data.warnings()});
                            data.bodyIndex(i);
                            data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
                        } else {

                            Util.checkState(data.formParams().isEmpty() || data.bodyIndex() == null, "Body parameters cannot be used with form parameters.%s", new Object[]{

     data.warnings()});
                        }
                    }
                }
                // 处理 @HeaderMap
                if (data.headerMapIndex() != null) {

                    checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]);
                }
                // 处理@QueryMap
                if (data.queryMapIndex() != null && Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {

                    checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
                }

                return data;
            }
        }

其中processAnnotationOnClass用于处理@RequestMapping注解,将注解中的访问URL地址设置到请求模板中。

    protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {

        if (clz.getInterfaces().length == 0) {

            // 获取RequestMapping 注解
            RequestMapping classAnnotation = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(clz, RequestMapping.class);
            if (classAnnotation != null && classAnnotation.value().length > 0) {

                String pathValue = Util.emptyToNull(classAnnotation.value()[0]);
                pathValue = this.resolve(pathValue);
                if (!pathValue.startsWith("/")) {

                    pathValue = "/" + pathValue;
                }

                data.template().uri(pathValue);
            }
        }

    }

processAnnotationsOnParameter 则用于处理方法上的参数注解:

    protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {

        boolean isHttpAnnotation = false;
        // 创建AnnotatedParameterContext 
        AnnotatedParameterContext context = new SpringMvcContract.SimpleAnnotatedParameterContext(data, paramIndex);
        Method method = (Method)this.processedMethods.get(data.configKey());
        Annotation[] var7 = annotations;
        int var8 = annotations.length;
        // 循环形参 
        for(int var9 = 0; var9 < var8; ++var9) {

            Annotation parameterAnnotation = var7[var9];
            // 处理形参,将@RequestParam 中方法的参数,添加到请求模板中。
            AnnotatedParameterProcessor processor = (AnnotatedParameterProcessor)this.annotatedArgumentProcessors.get(parameterAnnotation.annotationType());
            if (processor != null) {

                Annotation processParameterAnnotation = this.synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation, method, paramIndex);
                isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
            }
        }
        // 处理Multipart 表单数据
        if (!this.isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {

            TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
            if (this.conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {

                Expander expander = this.convertingExpanderFactory.getExpander(typeDescriptor);
                if (expander != null) {

                    data.indexToExpander().put(paramIndex, expander);
                }
            }
        }

        return isHttpAnnotation;
    }

最终,这个接口中的所有就被解析为了元数据,最终会依据这些信息创建方法处理器及请求模板。

&nbsp;
循环方法元数据,创建方法处理器。
&nbsp;