SpringCloudRPC远程调用核心原理:模拟Feign RPC动态代理的实现

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
final String url;

public MockRpcMethodHandler(String contextPath, String url)

{

this.contextPath = contextPath;

this.url = url;

}

/**

*功能:组装URL,完成REST RPC远程调用,并且返回JSON结果

*@param argv RPC方法的参数

*@return REST接口的响应结果

*@throws Throwable异常

*/

@Override

public Object invoke(Object[] argv) throws Throwable

{

/**

*组装REST接口URL

*/

String restUrl = contextPath + MessageFormat.format(url, argv);

log.info(“restUrl={}”, restUrl);

/**

*通过HttpClient组件调用REST接口

*/

String responseData = HttpRequestUtil.simpleGet(restUrl);

/**

*解析REST接口的响应结果,解析成JSON对象并且返回

*/

RestOut result = JsonUtil.jsonToPojo(responseData,

new TypeReference<RestOut>() {});

return result;

}

}

在模拟方法处理器实现类MockRpcMethodHandler的invoke(Object[])完成了以下3个工作:

(1)组装URL,将来自RPC的请求上下文路径(一般来自RPC客户端类级别注解)和远程调用的方法级别的URI路径拼接在一起,组成完整的URL路径。

(2)通过HttpClient组件(也可以是其他组件)发起HTTP请求,调用服务端的REST接口。

(3)解析REST接口的响应结果,解析成POJO对象(这里是JSON对象)并且返回。

模拟Feign的调用处理器InvocationHandler

==============================

调用处理器FeignInvocationHandler是一个相对简单的类,拥有一个非常重要的Map类型的成员dispatch,保存着RPC方法反射实例到其MethodHandler方法处理器的映射。

这里设计了一个模拟调用处理器MockInvocationHandler,用于模拟FeignInvocationHandler调用处理器,模拟调用处理器同样拥有一个Map类型的成员dispatch,负责保存RPC方法反射实例到模拟方法处理器MockRpcMethodHandler之间的映射。一个运行时MockInvocationHandler模拟调用处理器实例的dispatch成员的内存结构图如图3-7所示。

SpringCloudRPC远程调用核心原理:模拟Feign RPC动态代理的实现

图3-7 一个运行时MockInvocationHandler的dispatch成员的内存结构

MockInvocationHandler通过Java反射扫描模拟RPC远程调用接口MockDemoClient中的每一个方法的反射注解,组装出一个对应的Map映射实例,它的key值为RPC方法的反射实例,value值为MockRpcMethodHandler方法的处理器实例。

MockInvocationHandler的源代码如下:package com.crazymaker.demo.proxy.FeignMock;

//省略import

class MockInvocationHandler implements InvocationHandler

{

/**

*远程调用的分发映射:根据方法名称分发方法处理器

*key:远程调用接口的方法反射实例

*value:模拟的方法处理器实例

*/

private Map<Method, RpcMethodHandler> dispatch;

/**

*功能:代理对象的创建

*@param clazz被代理的接口类型

*@return代理对象

*/

public static T newInstance(Class clazz)

{

/**

*从远程调用接口的类级别注解中获取REST地址的contextPath部分

*/

Annotation controllerAnno =

clazz.getAnnotation (RestController.class);

if (controllerAnno == null)

{

return null;

}

String contextPath = ((RestController) controllerAnno).value();

//创建一个调用处理器实例

MockInvocationHandler invokeHandler = new MockInvocationHandler();

invokeHandler.dispatch = new LinkedHashMap<>();

/**

*通过反射迭代远程调用接口的每一个方法,组装MockRpcMethodHandler处理器

*/

for (Method method : clazz.getMethods())

{

Annotation methodAnnotation =

method.getAnnotation (GetMapping.class);

if (methodAnnotation == null)

{

continue;

}

/**

*从远程调用接口的方法级别注解中获取REST地址的URI部分

*/

String uri = ((GetMapping)methodAnnotation).name();

/**

*组装MockRpcMethodHandler模拟方法处理器

*注入REST地址的contextPath部分和URI部分

*/

MockRpcMethodHandler handler =

new MockRpcMethodHandler (contextPath, uri);

/**

*重点:将模拟方法处理器handler实例缓存到dispatch映射中

*key为方法反射实例,value为方法处理器

*/

invokeHandler.dispatch.put(method, handler);

}

//创建代理对象

T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(),

new Class<?>[]{clazz}, invokeHandler);

return proxy;

} /**

*功能:动态代理实例的方法调用

*@param proxy 动态代理实例

*@param method 待调用的方法

*@param args 方法实参

*@return 返回值

*@throws Throwable 抛出的异常

*/

@Override

public Object invoke(Object proxy,

Method method, Object[] args) throws Throwable

{

if (“equals”.equals(method.getName()))

{

Object other = args.length > 0 && args[0] != null ? args[0] : null;

return equals(other);

} else if (“hashCode”.equals(method.getName()))

{

return hashCode();

} else if (“toString”.equals(method.getName()))

{

return toString();

}

/**

*从dispatch映射中根据方法反射实例获取方法处理器

*/

RpcMethodHandler rpcMethodHandler = dispatch.get(method);

/**

*方法处理器组装URL,完成REST RPC远程调用,并且返回JSON结果

*/

return rpcMethodHandler.invoke(args);

}

}

模拟Feign的动态代理RPC的执行流程

====================

模拟调用处理器MockInvocationHandler的newInstance(…)方法创建一个调用处理器实例,该方法与JDK的动态代理机制的newInstance(…)方法没有任何关系,仅仅是一个模拟Feign的自定义的业务方法,该方法的逻辑如下:

(1)从RPC远程调用接口的类级别注解中获取请求URL地址的contextPath上下文根路径部分,如实例中的

http://crazydemo.com:7700/demo-provider/。

(2)通过迭代扫描RPC接口的每一个方法,组装出对应的MockRpcMethodHandler模拟方法处理器,并且缓存到dispatch映射中。

模拟方法处理器MockRpcMethodHandler实例的创建和映射过程如下:

(1)从对应的RPC远程调用方法的注解中取得URL地址的URI部分,如hello()方法的注解中的URI地址为api/demo/hello/v1。

(2)新建MockRpcMethodHandler模拟方法处理器,注入URL地址的contextPath上下文根路径部分和URI部分。

(3)将新建的方法处理器实例作为value缓存到调用处理器MockInvocationHandler的dispatch映射中,其key为对应的RPC远程调用方法的Method反射实例。

然后,由模拟Feign调用处理器MockInvocationHandler的invoke(…)方法负责完成方法处理器实例的调用,该invoke(…)方法是JDK的InvocationHandler的invoke(…)抽象方法的具体实现。当动态代理实例的RPC方法(如hello)被调用时,MockInvocationHandler的invoke(…)方法会根据RPC方法的反射实例从dispatch映射中取出对应的MockRpcMethodHandler方法处理器实例,由该方法的处理器完成对远程服务的RPC调用。

模拟Feign动态代理RPC调用(以hello方法为例)的执行流程如图3-8所示。

SpringCloudRPC远程调用核心原理:模拟Feign RPC动态代理的实现

图3-8 模拟Feign动态代理的RPC执行流程(以hello方法为例)

模拟动态代理RPC远程调用的测试

================

以下为对模拟Feign动态代理RPC的调用处理器、方法处理器的测试用例,代码如下:

package com.crazymaker.demo.proxy.FeignMock;

//省略import

@Slf4j

public class FeignProxyMockTester

{

/**测试用例/

@Test

public void test()

{

/**

*创建远程调用接口的本地JDK Proxy代理实例

*/

MockDemoClient proxy =

MockInvocationHandler.newInstance(MockDemoClient.class);

/**

*通过模拟接口完成远程调用

*/

RestOut responseData = proxy.hello();

log.info(responseData.toString());

/**

*通过模拟接口完成远程调用

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
toString());

/**

*通过模拟接口完成远程调用

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

[外链图片转存中…(img-fDkfB8aV-1714760827125)]

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

[外链图片转存中…(img-Zpa1njuz-1714760827125)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值