RMI 远程方法调用浅析

RMI 远程方法调用,本次实现的RMI是借助配置,将想要运行的方法表示出来,借由java反射机制(获取类、对象、运行方法)和代理(方法执行前后的连接和断开)。

整体分两个部分:服务器端和客户端。
服务器端:等待客户端连接,待客户端将所需的方法、对象等发送到服务器后进行方法的执行,接着将结果返回给客户端。
客户端:由客户配置好所需类、方法,当需要调用时连接服务器并发送所需信息,等待服务器端执行。

初始化过程:
因为用的代理时JDK代理,需要对应的接口,需要用户提前配置好。

public static void initRMIInterfaces(String filePath) throws Exception {
		PropertiesParser.loadProperties(filePath);
		Set<String> keys = PropertiesParser.keySet();
		for (String key : keys) {
			String interfaceName = key;
			String className = PropertiesParser.getValue(key);
			
			Class<?> interfaze = Class.forName(interfaceName);
			Class<?> klass = Class.forName(className);
			
			if (!interfaze.isInterface()) {
				throw new Exception("[" + interfaze.getName() + "]不是接口!");
			}
			if (!interfaze.isAssignableFrom(klass)) {
				throw new Exception("类[" + klass.getName() + "]不是[" + interfaze.getName() + "]的实现类!");
			}
			
			Object object = klass.newInstance();
			dealInterfaceMethod(interfaze, klass, object);
		}
	}

依据用户配置好的文件,获取接口名,该接口需要和类对应,要判断是否是接口,是否与所给类有关。若都满足,则要开始依据类处理接口的方法。

private static void dealInterfaceMethod(Class<?> interfaze, Class<?> klass, Object object)
			throws Exception {
		Method[] methods = interfaze.getDeclaredMethods();
		for (Method interfaceMethod : methods) {
			System.out.println("扫描到:" + interfaceMethod.toString());
			String key = String.valueOf(interfaceMethod.toString().hashCode());
			String methodName = interfaceMethod.getName();
			Class<?>[] parameterTypes = interfaceMethod.getParameterTypes();
			
			Method classMethod = klass.getDeclaredMethod(methodName, parameterTypes);
			RMIInterfaceDefinition rmiid = new RMIInterfaceDefinition(klass, object, classMethod);
			
			RMIInterfaceFactory.rmiInterPool.put(key, rmiid);
		}
	}

由获取的接口得到所有方法,依据接口的方法名、所需参数在类中找到对应方法,将这个方法和对应的id存起来以备后续由服务器调用。

代理的初始化:

public <T> T getProxy(Class<?> interfaze) {
		ClassLoader classLoader = interfaze.getClassLoader();
		Class<?>[] interfaces = new Class<?>[] {interfaze};
		
		return (T) Proxy.newProxyInstance(classLoader, interfaces, 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						if (RMIProxy.this.rmiClient == null) {
							throw new Exception("未定义RMIClient");
						}
						NodeAddress serverAddress = rmiClient.getAddress();
						Socket socket = rmiClient.connectToServer(
								serverAddress.getRmiIp(), serverAddress.getRmiPort());
						RMIClientCommunication rmiCommunication = new RMIClientCommunication(socket);
						
						// 连接服务器
						// 传递method信息和实参args
						// 等待并接收服务器返回的method执行结果
						return rmiCommunication.doRmiMethod(interfaze, method, args);
					}
				});
	}

这里代理用JDK代理,在调用方法中,先进行客户端连接服务器,连接成功后生成对应的communication并执行方法。

Object doRmiMethod(Class<?> interfaze, Method method, Object[] args)
			throws Throwable{
		System.out.println("method:" + method.toString());
		String methodString = String.valueOf(method.toString().hashCode());
		this.dos.writeUTF(methodString);
		this.dos.writeUTF(argsToString(args));
		
		String strResult = this.dis.readUTF();
		
		return ArgumentMaker.gson.fromJson(strResult, method.getGenericReturnType());
	}

执行方法过程就是将方法id,所需参数传给服务器,并获取结果,该结果由JSON处理。
服务器端的处理:

public void run() {
		try {
			String methodId = this.dis.readUTF();
			String strParameter = this.dis.readUTF();
			
			RMIInterfaceDefinition rmiid = RMIInterfaceFactory.getMethod(methodId);
			if (rmiid == null) {
				throw new Exception("未找到方法键:" + methodId);
			}
			
			Object object = rmiid.getObject();
			Method method = rmiid.getMethod();
			
			Object[] parameterValues = getParameterValues(method, strParameter);
			Object result = method.invoke(object, parameterValues);
			
			this.dos.writeUTF(ArgumentMaker.gson.toJson(result));
			}

服务器端是一个一直在监听的服务器,当客户端将方法id、所需参数发送后,依据id查找之前配置好的接口、类中的对应方法兵执行,再将执行结果JSON后发给客户端。
上述即RMI整体思路。

额外知识点:
参数存储:利用gson和HashMap<String, String>存储,HashMap的键为参数名,值为对应的参数值的gson字符串。
并且在客户端与服务器之间传递这个图时,也是将该图借助json字符串传递:
服务器端:

private static Type mapType = new TypeToken<Map<String, String>>() {}.getType();
public ArgumentMaker(String parameterString) {
		this.argMap = gson.fromJson(parameterString, mapType);
	}

取参数:

public Object getParameter(String name, Class<?> type) {
		String strValue = this.argMap.get(name);
		return gson.fromJson(strValue, type);
	}

客户端:
添加参数:

public ArgumentMaker add(String parameterName, Object parameterValue) {
		this.argMap.put(parameterName, gson.toJson(parameterValue));
		return this;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔幻音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值