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