Request和Response的封装这里就不写啦🤨
先说说反射
- 反射即Reflection,Java的反射是指程序在运行期间可以拿到一个对象的所有信息。 为什么说是运行期间呢,因为正常情况,如果我们要想运行一个对象的方法,那我们就需要传入这个对象的实例,不然无法编译成功
- 反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法
反射在RPCServer的应用
核心代码如下:
// 反射调用对应方法
Method method = userService.getClass().getMethod(request.getMethodName(), request.getParamsTypes());
Object invoke = method.invoke(userService, request.getParams());
-
JVM为每个加载的
class及interface创建了对应的Class实例来保存class及interface的所有信息。获取一个class对应的Class实例后,就可以获取该class的所有信息。通过Class实例获取class信息的方法称为反射(Reflection)。 -
userService是一个实例,使用
getClass()方法获取对应的Class实例。再通过getMethod(name, Class...):获取某个public的Method,而参数就是我们从服务器发来request中封装的方法名,以及参数类型。 -
然后对
Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例(在这里就是userService),后面的可变参数要与方法参数一致(在这里我们使用服务器封装的request获取方法参数)。
动态代理在RPCCLient的应用
RPCCLient代码如下:
public class RPCClient {
public static void main(String[] args) {
ClientProxy clientProxy = new ClientProxy("127.0.0.1", 8899);
UserService proxy = clientProxy.getProxy(UserService.class);
// 服务的方法1
User userByUserId = proxy.getUserByUserId(10);
System.out.println("从服务端得到的user为:" + userByUserId);
// 服务的方法2
User user = User.builder().userName("张三").id(100).sex(true).build();
Integer integer = proxy.insertUserId(user);
System.out.println("向服务端插入数据:"+integer);
}
}
- 使用Java标准库提供的动态代理功能,我们在运行期动态创建了UserService这一接口的实例。
- 动态代理是通过
Proxy创建代理对象,即使用方法getProxy(UserService.class)获取接口UserService的代理对象。 - 然后将接口方法“代理”给
InvocationHandler完成的。
- 在运行期动态创建一个
interface实例的方法如下:
- 定义一个
InvocationHandler实例,即文中clientProxy,它负责实现接口的方法调用;
ClientProxy代码如下:
@AllArgsConstructor
public class ClientProxy implements InvocationHandler {
// 传入参数Service接口的class对象,反射封装成一个request
private String host;
private int port;
// jdk 动态代理, 每一次代理对象调用方法,会经过此方法增强(反射获取request对象,socket发送至客户端)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// request的构建,使用了lombok中的builder,代码简洁
RPCRequest request = RPCRequest.builder().interfaceName(method.getDeclaringClass().getName())
.methodName(method.getName())
.params(args).paramsTypes(method.getParameterTypes()).build();
// 数据传输
RPCResponse response = IOClient.sendRequest(host, port, request);
//System.out.println(response);
return response.getData();
}
<T>T getProxy(Class<T> clazz){
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return (T)o;
}
}
-
通过
Proxy.newProxyInstance()创建interface实例,在这里我用了getProxy()封装,它需要3个参数:- 使用的
ClassLoader,通常就是接口类的ClassLoader; - 需要实现的接口数组(在这里我只放了一个接口,即
UserService.class),至少需要传入一个接口进去; - 用来处理接口方法调用的
InvocationHandler实例,在这里由于方法写在InvocationHandler实例内部,那便是this。
- 使用的
<T>T getProxy(Class<T> clazz){
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return (T)o;
}
- 将返回的
Object强制转型为接口。
UserService userService = clientProxy.getProxy(UserService.class);
动态代理实际上是JVM在运行期动态创建class字节码并加载的过程, 感觉到很神奇是因为JVM帮我们自动编写了一个实现类(不需要源码,可以直接生成字节码),并不存在可以直接实例化接口的黑魔法!!😁 大概如下
public class UserServiceDynamicProxy implements UserService {
InvocationHandler handler;
public UserServiceDynamicProxy(InvocationHandler handler) {
this.handler = handler;
}
public void getUserById(Integer id) {
handler.invoke(
this,
UserService.class.getMethod("getUserById", Integer.class),
new Object[] { name });
}
}
总之,Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例,动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的。
- 参考资料