美文网首页
如何阅读 Spring Cloud OpenFein 源码

如何阅读 Spring Cloud OpenFein 源码

作者: 冯文议 | 来源:发表于2022-10-22 06:53 被阅读0次

    背景

    一直以来,使用 Spring Cloud OpenFeign 都是阅读官方文档,虽然也大概知道其实现原理,但终究是没有"证据"的。关于 Spring 的源码阅读,自认为是一件十分令人头疼的事情。最近,在学习 Feign 的原生 API,乘此机会,也就阅读一下 Spring Cloud OpenFeign 的源码,并将分享出来,希望能帮到有需要的人吧。

    概述

    关于 Spring Cloud OpenFeign 源码的博客有很多,但是,不知道为什么,照着博客,一边读博客,一边读源码,还一边 debug,总是认为还有很多不清楚的地方。究其原因,我认为,博客都是按照源码的流程讲解,虽然附上了大段代码,可能还是无法清晰的理解。不知道你们是不是,反正我是这样的。

    目标

    首先,我们明确一下今天探究的问题:

    1. 我们知道,当我们使用 @FeignClient,是使用了JDK动态代理,那么是如何实现的,那一步创建的代理类。

    2. 当我们知道第一个问题后,我们就基本清楚整个流程了,那么,我们就可以手写一个简易的入门测试了。

    源码

    启动流程

    • org.springframework.cloud.openfeign.EnableFeignClients
    • org.springframework.cloud.openfeign.FeignAutoConfiguration
    • org.springframework.cloud.openfeign.FeignClientsRegistrar
    • org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
    • org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
    • org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
    • org.springframework.cloud.openfeign.Targeter
    • feign.Feign.Builder#build
    • feign.SynchronousMethodHandler.Factory
    • feign.ReflectiveFeign.ParseHandlersByName
    • feign.ReflectiveFeign#ReflectiveFeign
    • feign.Feign#newInstance
    • feign.ReflectiveFeign#newInstance
    • feign.InvocationHandlerFactory#create
    • feign.InvocationHandlerFactory.Default#create
    • feign.ReflectiveFeign.FeignInvocationHandler#FeignInvocationHandler

    贴上图吧,看看完整版的

    注册流程:

    01.jpg

    自动配置:

    02.jpg

    调用流程

    • feign.ReflectiveFeign.FeignInvocationHandler#invoke
    • feign.InvocationHandlerFactory.MethodHandler#invoke
    • feign.SynchronousMethodHandler#invoke
    03.jpg

    MyRpc

    经过上面的流程,我们手写一个 RPC。

    下面给出主要代码。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import({MyRpcRegister.class, MyRpcAutoConfig.class})
    public @interface EnableMyRpc {
    }
    

    getObject()

    @Data
    public class MyRpcFactoryBean implements FactoryBean<Object> {
    
        private String url;
    
        private String contextPath;
    
        private String name;
    
        private Class<?> type;
    
        private BeanFactory beanFactory;
    
        private MyClient myClient;
    
        @Override
        public Object getObject() {
    
            Map<Method, RpcBean> map = new HashMap<>();
    
            Method[] methods = type.getMethods();
    
            myClient = beanFactory.getBean(MyClient.class);
    
            for (Method method : methods) {
                Annotation[] annotations = method.getAnnotations();
                String httpMethod = "";
                String path = "";
                for (Annotation annotation : annotations) {
                    if (annotation.annotationType() == PostMapping.class) {
                        httpMethod = "POST";
                        path = ((PostMapping) annotation).value()[0];
                        break;
                    } else if (annotation.annotationType() == GetMapping.class) {
                        httpMethod = "GET";
                        path = ((GetMapping) annotation).value()[0];
                        break;
                    } else if (annotation.annotationType() == RequestMapping.class) {
                        RequestMapping requestMapping = ((RequestMapping) annotation);
                        httpMethod = requestMapping.method()[0].name();
                        path = requestMapping.value()[0];
                        break;
                    }
                }
                RpcBean rpcBean = new RpcBean()
                        .setUrl(url + contextPath)
                        .setPath(path)
                        .setHttpMethod(httpMethod)
                        .setMyClient(myClient)
                        ;
                map.put(method, rpcBean);
            }
    
            ClassLoader loader = type.getClassLoader();
    
            return Proxy.newProxyInstance(loader, new Class<?>[] {type}, new MyRpcInvocationHandler(map));
        }
    
        @Override
        public Class<?> getObjectType() {
            return type;
        }
    
    }
    

    handler

    @Slf4j
    public class MyRpcInvocationHandler implements InvocationHandler {
    
        private final Map<Method, RpcBean> map;
    
        public MyRpcInvocationHandler(Map<Method, RpcBean> map) {
            this.map = map;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            log.info("proxy handler");
    
            return request(method, args);
        }
    
        public Object request(Method method, Object[] args) {
            String result = "";
            RpcBean rpcBean = map.get(method);
            Parameter[] parameters = method.getParameters();
            Class<?> returnType = method.getReturnType();
            String url = rpcBean.getUrl() + rpcBean.getPath();
            String httpMethod = rpcBean.getHttpMethod();
            String param = getParam(httpMethod, parameters, args);
            log.info("url: [{}], param: [{}]", url, param);
            MyClient myClient = rpcBean.getMyClient();
            if ("POST".equals(httpMethod)) {
                result = myClient.post(url, param);
            } else if ("GET".equals(httpMethod)) {
                result = myClient.get(url, param);
            }
            if (StringUtils.hasText(result)) {
                return JsonUtils.convertObject(result, returnType);
            }
            return "";
        }
    
        public String getParam(String httpMethod, Parameter[] parameters, Object[] args) {
            if ("POST".equals(httpMethod)) {
                return JsonUtils.convertString(args[0]);
            } else if ("GET".equals(httpMethod)) {
                if (Objects.isNull(parameters) || parameters.length == 0
                        || Objects.isNull(args) || args.length == 0) {
                    return "";
                }
                String param = "";
                StringBuilder urlBuilder = new StringBuilder(param);
                for (int i = 0; i < parameters.length; i++) {
                    if (Objects.nonNull(args[i])) {
                        urlBuilder.append(String.format("%s=%s&", parameters[i].getName(), args[i]));
                    }
                }
                param = urlBuilder.toString();
                param = param.substring(0, param.length() - 1);
                return param;
            }
            return "";
        }
    }
    

    完整代码

    相关文章

      网友评论

          本文标题:如何阅读 Spring Cloud OpenFein 源码

          本文链接:https://www.haomeiwen.com/subject/qcyvzrtx.html