《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. 技术实现

  1. 反射机制
  2. Java动态代理
  3. Json的使用
  4. 获取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逻辑
posted @ 2023-05-26 14:39  Geek李  阅读(32)  评论(0)    收藏  举报