RMI框架的学习(引入模态框)

本文详细阐述了RMI(远程方法调用)的工作原理及其实现步骤,包括客户端和服务端的交互过程,以及如何通过XML文件映射远程方法。RMI简化了不同Java虚拟机间的通信,使得远程调用如同本地方法般便捷。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、RMI实现的原理

RMI(Remote Method Invoke)也叫做“远程方法调用”,他实现的原理是,当客户端向服务器发出“请求”时,把这个“请求”看作是一个普通的方法,并不是真正要执行的请求方法,当然,也可以理解为一个“信号”。在这个方法中要做的事,仅仅是发送给服务器端目标执行的方法的名字、参数等。而服务器端收到该方法名以及参数后,去找到该对应方法,反射执行并返回结果给客户端,也叫做“响应”,完成之后就关闭线程,采用的是“短连接”模式。

二、RMI实现的主要步骤:

1、给出一个接口,接口中是一些能被远程调用的方法;
2、可以将接口和对应的远程调用方法写成XML文件(方便解析找到相关方法);
3、在客户端方面,通过代理机制获取接口对象;
4、而在代理机制中,只需要启动一个简单的模态框,其他的给服务器传送方法名和参数的操作都放在模态框的初始化中(这样就避免了模态框显示后阻塞的问题);
5、服务端方面,侦听到客户端的连接,接收客户端发来的消息,根据得到的方法名和参数去找该方法,通过反射机制执行方法,并返回结果给客户端;
6、完成后关闭线程。


客户端代码:
public static <T> T getJDKPorxy(T target) {
		Class<?> klass = target.getClass();
		ClassLoader classLoader = klass.getClassLoader();
		Class<?>[] interfaces = klass.getInterfaces();
		
		T jdkProxy = (T)Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				
				MethodInvoker methodInvoker = new MethodInvoker();

				MyDialog myDialog = new MyDialog("等待服务器的响应··");
				myDialog.setMethodInvoker(methodInvoker)
						.setMethod(method)
						.setPara(new Object[] {"yyqx"});
				myDialog.showDailog();
						
				return myDialog.getResult();
			}
		});
		
		return jdkProxy;
	}

给服务器发送方法名和参数的操作放在模态框初始化中:

public MyDialog(String context) {
		super();
		initDailog(context);
	}

private MethodInvoker methodInvoker;
	private Method method;
	private Object[] para;
	private Object result;

	public MethodInvoker getMethodInvoker() {
		return methodInvoker;
	}

	public MyDialog setMethodInvoker(MethodInvoker methodInvoker) {
		this.methodInvoker = methodInvoker;
		return this;
	}

	public Method getMethod() {
		return method;
	}

	public MyDialog setMethod(Method method) {
		this.method = method;
		return this;
	}

	public Object[] getPara() {
		return para;
	}

	public MyDialog setPara(Object[] para) {
		this.para = para;
		return this;
	}
	
	public void showDailog() {
		setVisible(true);
		dealAction();
	}
	
	public void closeDailog() {
		dispose();
	}
	
	public void initDailog(String context) {
		Font font = new Font("宋体", Font.BOLD, 16);
		int width = context.length()*font.getSize() *2;
		int height = font.getSize() * 5;
		
		setSize(width, height);
		setLocationRelativeTo(null);
		setLayout(new BorderLayout());
		setUndecorated(true);
		
		JPanel jpnlMes = new JPanel();
		jpnlMes.setBackground(Color.GRAY);
		jpnlMes.setLayout(new BorderLayout());
		add(jpnlMes);
		
		JLabel jlblMes = new JLabel(context,JLabel.CENTER);
		jlblMes.setFont(font);
		jpnlMes.add(jlblMes,BorderLayout.CENTER);
		
	}
	
	public void dealAction() {
		addFocusListener(new FocusAdapter() {

			@Override
			public void focusGained(FocusEvent e) {
				result = methodInvoker.methodInvoke(method, new Object[] {"yyqx"});
				
				closeDailog();
			}
		});
	}
	
	public Object getResult() {
		return result;
	}

具体发送过程如下:

private static final String DEFAULT_IP = "127.0.0.1";
	private static final int DEFAULT_PORT = 51128;

	@SuppressWarnings("unchecked")
	@Override
	public <T> T methodInvoke(Method method, Object[] args) {
		
		Socket client = null;
		DataInputStream dis = null;
		DataOutputStream dos = null;
		
		try {
			client = new Socket(DEFAULT_IP, DEFAULT_PORT);
			
			dis = new DataInputStream(client.getInputStream());
			dos = new DataOutputStream(client.getOutputStream());

			dos.writeUTF(method.getName());
			dos.writeUTF(args.toString());
			
			String res = dis.readUTF();
			System.out.println("服务器发来的响应:" + res);
			
			return (T) res; 
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				dis.close();
				dos.close();
				client.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return null;
	}

服务端代码:
private static final int DEFAULT_PORT = 51128;
	
	private ServerSocket server;
	
	public RMIServer() {
	}
	
	public void start() {
		try {
			this.server = new ServerSocket(DEFAULT_PORT);
			new Thread(this).start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		Socket client = null;
		DataInputStream dis = null;
		DataOutputStream dos = null;
		
		try {
			System.out.println("服务器开始侦听···");
			client = this.server.accept();
			System.out.println("侦听到客户端["+ client.getInetAddress().getHostAddress()+"]");
			
			dis = new DataInputStream(client.getInputStream());
			dos = new DataOutputStream(client.getOutputStream());
			
			String meth = dis.readUTF();
			System.out.println("客户端发来的内容:" + meth);
			
			String args = dis.readUTF();
			System.out.println("客户端发来的内容:" + args);
			

			ScanMethod scanMethod = new ScanMethod();
			Object result = scanMethod.invokeMethod(meth, new Object[] {args});
			
			dos.writeUTF(result.toString());
			
			dis.close();
			dos.close();
			server.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

写远程调用方法的接口:

public interface IFunction{
	
	String function(String message);
}
public class Function implements IFunction{

	@Override
	public String function(String message) {
		return "能被远程调用的接口方法";
	}
}

服务端根据方法名和参数找到方法并执行的操作:

public Object invokeMethod(String methodName,Object[] parameter) {
		Document document = XMLParser.getDocument("/RMIMapping.xml");
		String tagName = "mapping";
		
		return new XMLParse() {
			
			@Override
			public Object dealElement(Element element, int index) {
				String strClass = element.getAttribute("class");
				Object result = null;
				try {
					Class<?> klass = Class.forName(strClass);
					Method method = klass.getDeclaredMethod(methodName, String.class);
					Object obj = klass.newInstance();
					result = method.invoke(obj, parameter);
					
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				return result;
			}
		}.parseTag(document, tagName);
		

备注:(xml文件的解析)

<RMIMapping>
	<mapping interface="com.xxxx.rmi.core.IFunction"
	class="com.xxxx.rmi.core.Function">
	</mapping>
</RMIMapping>
public abstract class XMLParse {
	
	private static volatile DocumentBuilder db;
	
	public XMLParse() {
	}
	
	private static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
		if (db == null) {
			synchronized (XMLParser.class) {
				if (db == null) {
					db = DocumentBuilderFactory
							.newInstance()
							.newDocumentBuilder();
				}
			}
		}
		return db;
	}
	
	public static Document getDocument(String xmlPath) {
		InputStream is = XMLParser.class.getResourceAsStream(xmlPath);
		if (is == null) {
			System.out.println("xmlPath[" + xmlPath + "]不存在!");
			return null;
		}
		
		return getDocument(is);
	}
	
	public static Document getDocument(InputStream is) {
		Document document = null;
		try {
			document = getDocumentBuilder().parse(is);
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		
		return document;
	}
	
	public abstract Object dealElement(Element element, int index);
	
	@SuppressWarnings("unused")
	public Object parseTag(Document document, String tagName) {
		NodeList nodeList = document.getElementsByTagName(tagName);
		for (int index = 0; index < nodeList.getLength(); index++) {
			Element ele = (Element) nodeList.item(index);
			return dealElement(ele, index);
		}
		return null;
	}

二、RMI的应用好处:

总的来说,RMI可以实现不同Java虚拟机之间的通信,核心操作就是“远程对象”,只要声明了某方法可以被远程操作,就可以实现一个虚拟机上去调用另一个虚拟机上的对象方法。
这是很方便的一点,对于客户端来说,只要发送了请求,即完成一个“本地方法”,就可以去最终实现目标远程方法的调用,不需要去管中间的通信过程。而服务端只需要根据客户端发来的消息去找到方法执行就行,一定程度上也减轻了服务端的负担。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值