背景
始终以来,应用 Spring Cloud OpenFeign 都是浏览官网文档,尽管也大略晓得其实现原理,但究竟是没有"证据"的。对于 Spring 的源码浏览,自认为是一件非常令人头疼的事件。最近,在学习 Feign 的原生 API,乘此机会,也就浏览一下 Spring Cloud OpenFeign 的源码,并将分享进去,心愿能帮到有须要的人吧。
概述
对于 Spring Cloud OpenFeign 源码的博客有很多,然而,不晓得为什么,照着博客,一边读博客,一边读源码,还一边 debug,总是认为还有很多不分明的中央。究其原因,我认为,博客都是依照源码的流程解说,尽管附上了大段代码,可能还是无奈清晰的了解。不晓得你们是不是,反正我是这样的。
指标
首先,咱们明确一下明天探索的问题:
- 咱们晓得,当咱们应用
@FeignClient
,是应用了JDK动静代理,那么是如何实现的,那一步创立的代理类。 - 当咱们晓得第一个问题后,咱们就根本分明整个流程了,那么,咱们就能够手写一个繁难的入门测试了。
源码
启动流程
- 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
贴上图吧,看看完整版的
注册流程:
主动配置:
调用流程
- feign.ReflectiveFeign.FeignInvocationHandler#invoke
- feign.InvocationHandlerFactory.MethodHandler#invoke
- feign.SynchronousMethodHandler#invoke
MyRpc
通过下面的流程,咱们手写一个 RPC。
上面给出次要代码。
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import({MyRpcRegister.class, MyRpcAutoConfig.class})public @interface EnableMyRpc {}
getObject()
@Datapublic 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
@Slf4jpublic 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 ""; }}
残缺代码