《MecRmi》框架
《MecRmi》框架——模仿RMI框架
1. 特别声明
本《MecRmi》框架是作为JavaSE练习而创造的,并不是为了“造方轮子”而制作的!!!
在过去的编程里,我用过许多的相同的”制式代码“,基于这种认知,为了方便之后的编程,产生了制造框架的意图,于是有了本框架的诞生
2. 《MecRmi》框架概述
RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。《MecRmi》的构造是基于RMI框架的,通过Java动态代理InvocationHandler和Proxy实现远程调用
3. 思路探索
对于客户端而言,RMI只要求客户端针对方法本身,产生一种错觉:方法是在本地被调用的;
对于服务器而言,RMI相当于要处理一个来自客户端的“请求”;这个请求针对某个方法。
由于上述工作过程涉及CS模式,则,必然要建立服务器,要连接,要传输信息等等。
对于客户端而言,上述功能要能实现,就必须依赖“代理”技术。
即,在客户端的代理执行相关方法时,实现:
1、连接服务器;
2、传递方法及实参数据;
3、等待并接收服务器回传的计算结果。
4. 技术实现
- 反射机制
- Java动态代理
- Json的使用
- 获取XML文件标签的属性
5. 技术简化说明
1. 反射机制
/*
*本方法通过反射机制获取xml文件中的接口和其对应的实现类,并输出里面的方法
*/
public void dealElement(Element element, int index) throws Exception {
//获取xml文件中name属性对应的接口名和class属性对应的类名
String interfaceName = element.getAttribute("name");
String className = element.getAttribute("class");
//反射获取两者的class
Class<?> interfaze = Class.forName(interfaceName);
Class<?> klass = Class.forName(className);
//反射申请类的对象
Object object = klass.getConstructor().newInstance();
//获取方法列表
Method[] methodsInInterface = interfaze.getDeclaredMethods();
for (Method methodInInterface : methodsInInterface) {
//获取方法名字
String methodName = methodInInterface.getName();
//获取参数列表的数组
Parameter[] parameters = methodInInterface.getParameters();
Class<?>[] parameterTypes = new Class<?>[parameters.length];
int i = 0;
//循环获取参数类型的数组
for (Parameter parameter : parameters) {
Class<?> parameterType = parameter.getType();
parameterTypes[i++] = parameterType;
}
//根据方法名和参数类型列表获取klass对象的方法并输出
Method method = klass.getDeclaredMethod(methodName, parameterTypes);
System.out.println(method);
}
}
2. Java动态代理
public <T> T getProxy(Class<?> interfaze) {
//用这interfaze类来加载代理类
ClassLoader classLoader = interfaze.getClassLoader();
//将interfaze.claas放到Class数组中,以便代理类实现里面的方法
Class<?>[] interfaces = new Class<?>[] { interfaze };
/*
*每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类 调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的 invoke方法来调用
这里是new一个实现了InvocationHandler接口的匿名内部类
*/
InvocationHandler h = new InvocationHandler() {
//相当于对代理类方法的执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method和args是在被代理类调用方法时传递过来的,args是参数列表(实参)
rmiClient.doRemoteInvoker(method, args);
//被代理类获得方法的返回值结果
return rmiClient.getResult();
}
};
//classLoader:被代理的类 interfaces方法存在的接口 h:一个InvocationHandler对象
//创造代理类
return (T) Proxy.newProxyInstance(classLoader, interfaces, h);
}
3. Json的使用
package com.mec.util;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
public class ArgumentMaker {
//创建Gson对象,以便使用
public static final Gson gson = new GsonBuilder().create();
// 下面的mapType是专门为gson使用的!因为在程序运行时泛型会被抹除,所以我们用比class更底层的Type来处理
// 当使用gson转换“泛型”对象时,告知gson泛型类型中的泛型。
private static final Type mapType = new TypeToken<Map<String, String>>() {}.getType();
private Map<String, String> parameterPool;
public ArgumentMaker(String string) {
// 以现在要实现的问题为例,需要让gson知道,将string转换成Map对象,而这个Map
// 本身是存在两个泛型的,需要明确这两个泛型类型。通过mapType就可以说明。
this.parameterPool = gson.fromJson(string, mapType);
//gson.fromJson(string, mapType); 将string转换成mapType类型
// 要特别注意的是,此时this.paramterPool中的内容是:字符串 -> 字符串
}
public ArgumentMaker() {
this.parameterPool = new HashMap<String, String>();
}
//将字符串参数类型转换为原本的参数类型
@SuppressWarnings("unchecked")
public <T> T get(String parameterName, Class<?> parameterType) throws Exception {
String strParameterValue = this.parameterPool.get(parameterName);
if (strParameterValue == null) {
throw new Exception("没有名为[" + parameterName + "]的形参!");
}
return (T) gson.fromJson(strParameterValue, parameterType);
}
//重载get方法,参数类型用Type类型
@SuppressWarnings("unchecked")
public <T> T get(String parameterName, Type parameterType) throws Exception {
String strParameterValue = this.parameterPool.get(parameterName);
if (strParameterValue == null) {
throw new Exception("没有名为[" + parameterName + "]的形参!");
}
return (T) gson.fromJson(strParameterValue, parameterType);
}
public ArgumentMaker add(String parameteraName, Object parameterValue) {
this.parameterPool.put(parameteraName, gson.toJson(parameterValue));
return this;
}
@Override
public String toString() {
return gson.toJson(this.parameterPool);
}
}
4. XML标签属性获取
<?xml version="1.0" encoding="UTF-8"?>
<interfaces>
<interface name="com.mec.source.core.ISourceHolderFunction"
class="com.mec.source.core.SourceHolderRequesterFunction"></interface>
<interface name="com.mec.source.core.ISourceRequesterFunction"
class="com.mec.source.core.SourceHolderRequesterFunction"></interface>
</interfaces>
public abstract class XMLParser {
private static DocumentBuilder documentBuilder;
private static File xmlFile;
//静态实现documentBuilder的加载
static {
try {
XMLParser.documentBuilder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
} catch (Exception e) {
e.printStackTrace();
}
}
public XMLParser() {
}
//交给实现类去完成标签属性获取后的逻辑操作
public abstract void dealElement(Element element, int index) throws Exception;
//文件加载
public void parse(Document document, String tagname) throws Exception {
NodeList nodeList = document.getElementsByTagName(tagname);
for (int index = 0; index < nodeList.getLength(); index++) {
Element element = (Element) nodeList.item(index);
dealElement(element, index);
}
}
//标签加载
public void parse(Element element, String tagname) throws Exception {
NodeList nodeList = element.getElementsByTagName(tagname);
for (int index = 0; index < nodeList.getLength(); index++) {
Element ele = (Element) nodeList.item(index);
dealElement(ele, index);
}
}
//根据路径获取文件Document
public static Document getDocument(String xmlPath) throws Exception {
InputStream is = XMLParser.class.getResourceAsStream(xmlPath);
if (is == null) {
throw new Exception("XML[" + xmlPath + "]文件不存在!");
}
return XMLParser.documentBuilder.parse(is);
}
}
package com.mec.rmi.core;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Element;
import com.mec.util.XMLParser;
/*
*RmiImplClassFactory通过调用initRmi方法完成xml文件里面接口和实现类的获取
*/
public class RmiImplClassFactory {
private static final Map<Integer, MethodBeanDefinition> methodPool;
static {
methodPool = new HashMap<Integer, MethodBeanDefinition>();
}
public RmiImplClassFactory() {
}
public static void initRmi(String interfaceConfigXml) throws Exception {
new XMLParser() {
@Override
public void dealElement(Element element, int index) throws Exception {
String interfaceName = element.getAttribute("name");
String className = element.getAttribute("class");
//代码省略......
}
}.parse(XMLParser.getDocument(interfaceConfigXml), "interface");
}
static MethodBeanDefinition getMethodBean(int methodKey) {
return methodPool.get(methodKey);
}
}
5. IListener和ISpeaker
package com.mec.util;
public interface IListener {
void acceptMessage(String message);
}
package com.mec.util;
public interface ISpeaker {
void addListener(IListener listener);
void removeListener(IListener listener);
void publish(String message);
}
- 这两个接口配合使用,需要发送消息的类实现ISpeaker,将接受消息的类放入List
中 - 接收消息的类实现IListener,当“发送者”publish消息后,接收者就会执行acceptMessage逻辑

浙公网安备 33010602011771号