Spring-Cloud-Feign源码阅读(2)-FeignClient是如何解析的

博主阅读代码的版本是io.github.openfeign:9.7.0、spring-cloud-openfeign:2.0.2.RELEASE

一、Feign的协议接口-Contract

package feign;

public interface Contract {
    //将类解析并验证生成MethodMetadataList
    List<MethodMetadata> parseAndValidatateMetadata(Class<?> var1);
    ...
}

    Contract类是在io.github.openfeign:feign-core依赖包,是一个接口,仅有一个需要实现的方法,将类解析并验证生成MethodMetadataList。

package feign;

//方法元数据类
public final class MethodMetadata implements Serializable {
    private static final long serialVersionUID = 1L;
    private String configKey;
    //http请求的返回类型
    private transient Type returnType;
    //该方法中第几个参数作为http请求的url
    private Integer urlIndex;
    //该方法中第几个参数作为http请求的body
    private Integer bodyIndex;
    //该方法中第几个参数作为http请求的键值对类型的header
    private Integer headerMapIndex;
    //该方法中第几个参数作为http请求的键值对类型的query
    private Integer queryMapIndex;
    private boolean queryMapEncoded;
    //http请求body的类型
    private transient Type bodyType;
    //http请求模板  重点
    private RequestTemplate template = new RequestTemplate();
    private List<String> formParams = new ArrayList();
    private Map<Integer, Collection<String>> indexToName = new LinkedHashMap();
    private Map<Integer, Class<? extends Expander>> indexToExpanderClass = new LinkedHashMap();
    private Map<Integer, Boolean> indexToEncoded = new LinkedHashMap();
    private transient Map<Integer, Expander> indexToExpander;
    ...
}
package feign;

public final class RequestTemplate implements Serializable {
    private static final long serialVersionUID = 1L;
    //http请求键值对类型的query参数
    private final Map<String, Collection<String>> queries = new LinkedHashMap();
    //http请求键值对类型的headers
    private final Map<String, Collection<String>> headers = new LinkedHashMap();
    //请求方式
    private String method;
    //请求url
    private StringBuilder url = new StringBuilder();
    //编码方式
    private transient Charset charset;
    //http请求body
    private byte[] body;
    //http请求字符串类型的body
    private String bodyTemplate;
    private boolean decodeSlash = true;
    private CollectionFormat collectionFormat;
    ...
}

    MethodMetadata类是一个承上启下的类有点不太好理解,RequestTemplate类是一个http请求的封装类,包含必要的url、method、header、body和常见的queries(url?后面&方式拼接的参数)等属性,其对象是一个最终要发起的http请求。

    Contract类将一个clazz(Class类型的参数)解析成多个MethodMetadata。因为一个clazz会有多个method,所以会解析成多个MethodMetadata对象,每个MethodMetadata对象都一个RequestTemplate对象。RequestTemplate对象是一个最终被调用的请求。结合MethodMetadata代码看,就是clazz的方法与最终要发起的http请求属性之间的映射。

    Feign的协议Contract就是将一个类的各个方法(暂定义目标方法)解析成不同的RequestTemplate,MethodMetadata只是一个过程对象,MethodMetadata对象urlIndex、bodyIndex、headerMapIndex、queryMapIndex都会在发起请求之前把目标方法参数里的值设置到RequestTemplate相应的属性里。

二、Feign的注解抽象协议-BaseContract

package feign;

public abstract static class BaseContract implements Contract {
    
    //实现Contract接口方法 
    public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
        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();
        //获取clazz的目标方法
        Method[] var3 = targetType.getMethods();
        int var4 = var3.length;
        //遍历目标方法
        for(int var5 = 0; var5 < var4; ++var5) {
            Method method = var3[var5];
            //对方法做过滤 1、不是Object类的方法  2、修饰符为public、private、protect方法  3、不是 interface的且修饰符为default方法 
            if (method.getDeclaringClass() != Object.class && (method.getModifiers() & 8) == 0 && !Util.isDefault(method)) {
            //调用单个目标方法的解析方法
            MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method);
            Util.checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s", new Object[]{metadata.configKey()});
            result.put(metadata.configKey(), metadata);
            }
        }
        return new ArrayList(result.values());
    }

    //单个目标方法的解析
    protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) 
    {
        //初始化返回
        MethodMetadata data = new MethodMetadata();
        data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
        data.configKey(Feign.configKey(targetType, method));
        if (targetType.getInterfaces().length == 1) {
            this.processAnnotationOnClass(data, targetType.getInterfaces()[0]);
        }
        
        //根据类上面的注解设置MethodMetadata属性
        this.processAnnotationOnClass(data, targetType);
        Annotation[] var4 = method.getAnnotations();
        int var5 = var4.length;

        //根据方法上面的注解设置MethodMetadata属性
        for(int var6 = 0; var6 < var5; ++var6) {
            Annotation methodAnnotation = var4[var6];
            this.processAnnotationOnMethod(data, methodAnnotation, method);
        }

        Util.checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", new Object[]{method.getName()});
        Class<?>[] parameterTypes = method.getParameterTypes();
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        int count = parameterAnnotations.length;

        //根据方法参数上面的注解设置MethodMetadata属性
        for(int i = 0; i < count; ++i) {
            boolean isHttpAnnotation = false;
            if (parameterAnnotations[i] != null) {
                isHttpAnnotation = this.processAnnotationsOnParameter(data, parameterAnnotations[i], i);
            }

            if (parameterTypes[i] == URI.class) {
                data.urlIndex(i);
            } else if (!isHttpAnnotation) {
                Util.checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters.", new Object[0]);
                Util.checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", new Object[]{method});
                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 && Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
            checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
        }

        return data;
    }

    //根据类上面的注解设置MethodMetadata属性
    protected abstract void processAnnotationOnClass(MethodMetadata var1, Class<?> var2);

    //根据方法上面的注解设置MethodMetadata属性
    protected abstract void processAnnotationOnMethod(MethodMetadata var1, Annotation var2, Method var3);

    //根据方法参数上面的注解设置MethodMetadata属性
    protected abstract boolean processAnnotationsOnParameter(MethodMetadata var1, Annotation[] var2, int var3);
}

    BaseContract是Contract的内部静态抽象类,实现了Contract接口,对目标类做了一些验证,后面将整个解析步骤分解为一下步。

    (1)解析类的注解。

    (2)解析方法的注解。

    (3)解析方法参数上的注解。

三、Spring-cloud-Feign的协议-SpringMvcContract

package org.springframework.cloud.openfeign.support;

public class SpringMvcContract extends BaseContract implements ResourceLoaderAware {

    private static final String ACCEPT = "Accept";
    private static final String CONTENT_TYPE = "Content-Type";
    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
    private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
    private final Map<String, Method> processedMethods;
    private final ConversionService conversionService;
    private final Expander expander;
    private ResourceLoader resourceLoader;

    //AnnotatedParameterProcessors的构造方法
    public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
        this(annotatedParameterProcessors, new DefaultConversionService());
    }
    
    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;
                }
                //将@RequestMapping注解的值插入到MethodMetadata的RequesTemplate的url里
                data.template().insert(0, pathValue);
            }
        }

    }

    protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
        //判断方法上的注解是否是@RequestMapping 或者 注解上包含了@RequestMapping 例如@GetMapping等
        if (RequestMapping.class.isInstance(methodAnnotation) || methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
            RequestMapping methodMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
            //获取注解的请求方式
            RequestMethod[] methods = methodMapping.method();
            if (methods.length == 0) {
                methods = new RequestMethod[]{RequestMethod.GET};
            }

            this.checkOne(method, methods, "method");
            //设置MethodMetadata的RequesTemplat的method
            data.template().method(methods[0].name());
            this.checkAtMostOne(method, methodMapping.value(), "value");
            if (methodMapping.value().length > 0) {
                String pathValue = Util.emptyToNull(methodMapping.value()[0]);
                if (pathValue != null) {
                    pathValue = this.resolve(pathValue);
                    if (!pathValue.startsWith("/") && !data.template().toString().endsWith("/")) {
                        pathValue = "/" + pathValue;
                    }

                    //将注解的vaue拼接到MethodMetadata的RequesTemplate的url后面
                    data.template().append(pathValue);
                }
            }
           
            //通过方法上@Produces注解设置MethodMetadata的RequesTemplate的header中Accept值
            this.parseProduces(data, method, methodMapping);
            //通过方法上@Produces注解设置MethodMetadata的RequesTemplate的header中Content-Type值
            this.parseConsumes(data, method, methodMapping);
            //通过方法上@Produces注解设置MethodMetadata的RequesTemplate的header
            this.parseHeaders(data, method, methodMapping);
            data.indexToExpander(new LinkedHashMap());
        }
    }

    protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
        boolean isHttpAnnotation = false;
        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];
            //通过AnnotatedParameterProcessor解析方法参数上的注解
            AnnotatedParameterProcessor processor = (AnnotatedParameterProcessor)this.annotatedArgumentProcessors.get(parameterAnnotation.annotationType());
            if (processor != null) {
                Annotation processParameterAnnotation = this.synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation, method, paramIndex);
                isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
            }
        }

        if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null && this.conversionService.canConvert(method.getParameterTypes()[paramIndex], String.class)) {
            data.indexToExpander().put(paramIndex, this.expander);
        }

        return isHttpAnnotation;
    }
}

        spring-cloud-openfeign:2.0.2.RELEASE仅有3个AnnotatedParameterProcessor。

    (1)PathVariableParameterProcessor

package org.springframework.cloud.openfeign.annotation;

public class PathVariableParameterProcessor implements AnnotatedParameterProcessor {

    //支持@PathVariable注解
    private static final Class<PathVariable> ANNOTATION = PathVariable.class;

    public PathVariableParameterProcessor() {
    }

    public Class<? extends Annotation> getAnnotationType() {
        return ANNOTATION;
    }

    public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
        //获取@PathVariable注解的value
        String name = ((PathVariable)ANNOTATION.cast(annotation)).value();
        Util.checkState(Util.emptyToNull(name) != null, "PathVariable annotation was empty on param %s.", new Object[]{context.getParameterIndex()});
        context.setParameterName(name);
        MethodMetadata data = context.getMethodMetadata();
        String varName = '{' + name + '}';
        if (!data.template().url().contains(varName) && !this.searchMapValues(data.template().queries(), varName) && !this.searchMapValues(data.template().headers(), varName)) {
            //将name设置到MethodMetadata的RequesTemplate的form表单参数里面? 博主还有点疑问
            data.formParams().add(name);
        }

        return true;
    }

    (2)RequestHeaderParameterProcessor

package org.springframework.cloud.openfeign.annotation;

public class RequestHeaderParameterProcessor implements AnnotatedParameterProcessor {
    //支持@RequestHeader注解
    private static final Class<RequestHeader> ANNOTATION = RequestHeader.class;

    public RequestHeaderParameterProcessor() {
    }

    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();
        //如果加了@RequestHeader的参数类型是Map
        if (Map.class.isAssignableFrom(parameterType)) {
            Util.checkState(data.headerMapIndex() == null, "Header map can only be present once.", new Object[0]);
            //设置MethodMetadata的RequesTemplate的键值对类型的header参数下标
            data.headerMapIndex(parameterIndex);
            return true;
        } else {
            //获取@RequestHeader的name
            String name = ((RequestHeader)ANNOTATION.cast(annotation)).value();
            Util.checkState(Util.emptyToNull(name) != null, "RequestHeader.value() was empty on parameter %s", new Object[]{parameterIndex});
            context.setParameterName(name);
            Collection<String> header = context.setTemplateParameter(name, (Collection)data.template().headers().get(name));
            //设置MethodMetadata的RequesTemplate的键值对类型的header单个属性
            data.template().header(name, header);
            return true;
        }
    }
}

    (3)RequestParamParameterProcessor

package org.springframework.cloud.openfeign.annotation;

public class RequestParamParameterProcessor implements AnnotatedParameterProcessor {
    
    //支持@RequestParam注解
    private static final Class<RequestParam> ANNOTATION = RequestParam.class;

    public RequestParamParameterProcessor() {
    }

    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();
        //如果加了@RequestParam的参数类型是Map
        if (Map.class.isAssignableFrom(parameterType)) {
            Util.checkState(data.queryMapIndex() == null, "Query map can only be present once.", new Object[0]);
            //设置MethodMetadata的RequesTemplate的键值对类型的query参数下标
            data.queryMapIndex(parameterIndex);
            return true;
        } else {
            RequestParam requestParam = (RequestParam)ANNOTATION.cast(annotation);
            String name = requestParam.value();
            Util.checkState(Util.emptyToNull(name) != null, "RequestParam.value() was empty on parameter %s", new Object[]{parameterIndex});
            context.setParameterName(name);
            Collection<String> query = context.setTemplateParameter(name, (Collection)data.template().queries().get(name));
            //设置MethodMetadata的RequesTemplate的键值对类型的query单个属性
            data.template().query(name, query);
            return true;
        }
    }
}

    SpringMvcContract继承了BaseContract类,重写了以下三个方法。

    (1)processAnnotationOnClass 解析类的注解的方法。

      支持类上的@RequestMapping注解

    (2)processAnnotationOnMethod 解析方法的注解。

      支持方法上的@RequestMapping注解、以及@GetMapping、@PostMapping等包含了@RequestMapping的注解

    (3)processAnnotationsOnParameter 解析方法参数上的注解。

    支持方法参数上的@PathVariable、@RequestHeader、@RequestParam注解

四、总结

    io.github.openfeign:9.7.0、spring-cloud-openfeign:2.0.2.RELEASE版本有点旧了,但是在新版本中的实现不会有太多的改变。还有只需要实现一个AnnotatedParameterProcessor,再定义一个SpringMvcContract的bean,将自定义的AnnotatedParameterProcessor传入构造方法,feignClient就支持自定义的注解。

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值