《一线大厂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所示。
图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所示。
图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面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!