前提,J2SDK安装, 本例使用的是1.4.2_07。
RMI主要用在分布式应用中,一般情况下的使用是这样子,即业务逻辑在Server端的类中实现,而使用者即客户端则主要的调用Server提供的方法来得到结果。
下面就写一下这个例子,主要实现的功能是打印个1+2=3的等式。
哦,这儿要先想一下,即,对Client来讲,它需要知道Server提供了哪些东西,这个由一个接口来进行约束,即Server提供并实现了该接口里面的方法,而Client则参照该接口进行调用,就是这样一个思路。
那么Client是如何得到这些方法的实现呢,这就要归功于RMI,在Server那边同时提供一个RMI端口监听程序,来接受Client的呼叫调用。RMI同时建立一个基本框架。
这样一个思路应该比较明显,即建立一个Server程序放在RMI的框架中用于监听, 然后建立一个接口来约束基本方法的实现,Server对该接口进行了实现,Client则通过呼叫Server程序来调用该实现。
好了,如上所示,先建立一个接口,因为这个其实才是应用的业务核心所在:
RMIInterface.java:
import java.rmi.*;
public interface RMIInterface extends Remote{
// 实现一个加法
public int sum(int a, int b) throws RemoteException;
}
然后对该接口实现:
RMIInterfaceImpl.java:
import java.rmi.*;
import java.rmi.server.*;
public class RMIInterfaceImpl extends UnicastRemoteObject implements RmiSample{
public RMIInterfaceImpl () throws RemoteException{ super(); }
// 在这儿对该方法进行了实现
public int sum(int a, int b) throws RemoteException{
return a + b;
}
}
这样实现了以后,在Client那边才能得到想要的结果,下面建立RMIServer程序,如下:
RMIServer.java:
import java.rmi.*;
import java.rmi.registry.*;
public class RMIServer{
public static void main(String[] args) {
try{
LocateRegistry.createRegistry(8808); // 注册端口
RMIInterfaceImpl server = new RMIInterfaceImpl();
// Binding
Naming.rebind("//localhost:8808/RMI", server); // 将实现类绑到一个名字上去
}catch(java.net.MalformedURLException me){
System.out.println("Malformed URL:" + me.toString());
}catch(RemoteException e){
System.out.println("Remote Exception:" + e.toString());
}
}
}
这样Server端的相差程序已经建立OK啦,下面就建立Client那边的程序,其实就是一个调用,到某个地址那里去找RMI的监听,然后取得对象,来调用得到结果:
RMIClient.java:
import java.rmi.*;
import java.rmi.server.*;
public class RMIClient {
public static void main(String[] args){
try {
String url = "//190.0.0.112:8808/SAMPLE-SERVER";
RMIInterface RMIObject = (RMIInterface)Naming.lookup(url); // 寻得对象
System.out.println(" 1 + 2 = " + RMIObject.sum(1,2) );
} catch (RemoteException exc) {
System.out.println("Lookup error: " + exc.toString());
} catch (java.net.MalformedURLException ex) {
System.out.println("Malformed URL: " + ex.toString());
} catch (java.rmi.NotBoundException e) {
System.out.println("Not Bound: " + e.toString());
}
}
}
这里可能会有一个问题,就是对于这一句不免会有一点想法:
RMIInterface RMIObject = (RMIInterface)Naming.lookup(url); // 寻得对象
嘿嘿,这个嘛,偶目前也有点不清楚,怎么用接口来取得对象。
好了,程序已经编完啦,只有四个类,下面编译,在本例中,四个类都放在缺省包下,编写一个用于编译的批处理文件,和这四个JAVA文件放在一起,至于这个并不重要,只是因为懒一点,所以写了,省得每次打那么多字:
Compile.bat:
javac *.java
rmic -d . RMIInterfaceImpl
编译完成后会出现六个类,这是怎么回事儿吶,原来多了两个类:
RMIInterfaceImpl_Skel.class
RMIInterfaceImpl_Stub.class
就是RMI的框架需要的类啦,不过在这里,Client只需要stub类就行了,别问我为什么,不知道,哈哈。
好了,下面就是分成两组,这儿可能.NET的兄弟又在说J2EE就是麻烦,呵呵,这是最无聊且没营养的一种说法,技术本身没有绝对的优劣,只有适合不适合于应用。
这六个类文件,其中Client端需要三个:
RMIInterface.class
RMIClient.class
RMIInterfaceImpl_Stub.class
服务端则需要五个文件:
RMIInterface.class
RMIInterfaceImpl.class
RMIServer.class
RMIInterfaceImpl_Skel.class
RMIInterfaceImpl_Stub.class
这儿可能再建立两个文件夹来分别copy一下,分成server和client应该比较容易理解和发布吧,呵呵,这个动作完全可以随手写个程序来完成的,由于偶没什么大的应用,所以就懒得写啦。
该动作的程序思路应该是这样子,即生成接口,在编译接口的时候,C和S各一份,然后做实现和Server程序,扔S那里去,而做Client调用程序的时候则扔C那里去,在用RMIC对实现建立框架的时候,则C和S也各一份,如此,就形成服务端和客户端分开的情况了。
当然Server的监听程序和Client的调用程序自动生成的东西比较多,嘿嘿
好了,对每个目录进行打包:
/server
Jar cvf server.jar *.class
/client
Jar cvf server.jar *.class
这儿其实可以不用打包,直接来用,不这不利于发布,也就写一下吧。
在生成server.jar和client.jar后,就可以进行“分布式”应用啦,哈哈,分布式其实挺简单的,就是在不同的机器单元上协同起来做同一件事情嘛。
分别为Server和Client写一个批处理,该例中,批处理和相应的jar文件是放在一起的:
Server.bat:
java -classpath "server.jar" RMIServer
Client.bat:
java -classpath "client.jar" RMIClient
OK,先运行server.bat,然后运行N次client.bat,不过每次的结果都是一样的,呵呵