RMI(即Remote Method Invoke 远程方法调用)。在Java中,只要一个类extends了java.rmi.Remote接口,便可成为存在于服务器端的远程对象,供客户端访问并提供必定的服务。JavaDoc描述:Remote 接口用于标识其方法能够从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”(扩展 java.rmi.Remote 的接口)中指定的这些方法才可远程使用。 java
注意:extends了Remote接口的类或者其余接口中的方法如果声明抛出了RemoteException异常,则代表该方法可被客户端远程访问调用。 服务器
同时,远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问得到远程对象时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所得到的这个拷贝称为“存根”,而服务器端自己已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通讯,而骨架也可认为是服务器端的一个代理,用于接收客户端的请求以后调用远程方法来响应客户端的请求。 app
RMI 框架的基本原理大概以下图,应用了代理模式来封装了本地存根与真实的远程对象进行通讯的细节。框架
(该图转自51CTO)分布式
1. Server端:ide
HelloServer | RMI服务器端 |
IHello | RMI接口 |
HelloImpl | RMI实现 |
HelloServer.javathis
package com.rmidemo.server; import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; public class HelloServer { public static void main(String args[]) { try { // 建立一个远程对象 IHello rhello = new HelloImpl(); // 本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099),必不可缺的一步,缺乏注册表建立,则没法绑定对象到远程注册表上 LocateRegistry.createRegistry(8888); // 把远程对象注册到RMI注册服务器上,并命名为RHello // 绑定的URL标准格式为:rmi://host:port/name(其中协议名能够省略,下面两种写法都是正确的) Naming.bind("rmi://localhost:8888/RHello", rhello); System.out.println("cout<<<<< INFO:远程IHello对象绑定成功!"); } catch (RemoteException e) { System.out.println("建立远程对象发生异常!"); e.printStackTrace(); } catch (AlreadyBoundException e) { System.out.println("发生重复绑定对象异常!"); e.printStackTrace(); } catch (MalformedURLException e) { System.out.println("发生URL畸形异常!"); e.printStackTrace(); } } }
IHello.javaspa
package com.rmidemo.server; import java.rmi.Remote; import java.rmi.RemoteException; public interface IHello extends Remote { /** * 简单的返回“Hello World!"字样 * * @return 返回“Hello World!"字样 * @throws java.rmi.RemoteException */ public String helloWorld() throws RemoteException; /** * 一个简单的业务方法,根据传入的人名返回相应的问候语 * * @param someBodyName * 人名 * @return 返回相应的问候语 * @throws java.rmi.RemoteException */ public String sayHelloToSomeBody(String someBodyName) throws RemoteException; }
HelloImpl.java.net
package com.rmidemo.server; import java.io.Serializable; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements IHello, Serializable { /** * serialVersionUID */ private static final long serialVersionUID = 3016980098018700517L; protected HelloImpl() throws RemoteException { super(); } @Override public String helloWorld() throws RemoteException { System.out.println("call 1"); return "word"; } @Override public String sayHelloToSomeBody(String someBodyName) throws RemoteException { System.out.println("call 2"); return "666" + someBodyName; } }
1. Client端:代理
HelloClient | 客户端请求类 |
IHello | 远程对象的接口 |
HelloClient.java:
package com.rmidemo.client; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import com.rmidemo.server.IHello; public class HelloClient { public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException { IHello remoteHello = (IHello) Naming.lookup("rmi://localhost:8888/RHello"); System.out.println(remoteHello.helloWorld()); System.out.println(remoteHello.sayHelloToSomeBody("666")); } }
IHello.java:
package com.rmidemo.server; import java.rmi.Remote; import java.rmi.RemoteException; public interface IHello extends Remote { /** * 简单的返回“Hello World!"字样 * * @return 返回“Hello World!"字样 * @throws java.rmi.RemoteException */ public String helloWorld() throws RemoteException; /** * 一个简单的业务方法,根据传入的人名返回相应的问候语 * * @param someBodyName * 人名 * @return 返回相应的问候语 * @throws java.rmi.RemoteException */ public String sayHelloToSomeBody(String someBodyName) throws RemoteException; }
启动服务端:——>
启动客户端:——>
这里讲述的是基于JDK1.5的RMI程序搭建,更简单的说是一个 HelloWorld RMI。
1. 这里是基于JDK1.5的,节省了繁琐的手工编译(生成桩和骨架)。不像1.4以前的RMI。
2. 这里是把客户端和服务器端的两个程序,分布在两个独立的程序里面,而不是同一个package下面。是真正的分布式。
3. 这里不过多阐述原理,这只是一个Hello World!!
好,如下是步骤:
1. 在Eclipse里面建立一个server 端的project。而后,建立一个接口,这个接口是你要向client端开放的方法定义。它叫作:UserManagerInterface,并且必须继承Remote接口。
服务器端
Account.java | 可供序列化的类 |
UserManagerInterface.java | 接口 |
UserManagerImpl.java | 接口实现 |
ServerMain.java | 主类 |
Account.java:
package com.rmidemo.chenjun.bean; import java.io.Serializable; public class Account implements Serializable, Cloneable { /** * serialVersionUID */ private static final long serialVersionUID = -8797228446371683285L; private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
UserManagerInterface.java:
package com.rmidemo.chenjun.stub; import java.rmi.Remote; import java.rmi.RemoteException; import com.rmidemo.chenjun.bean.Account; public interface UserManagerInterface extends Remote { public String getUserName() throws RemoteException; public Account getAdminAccount() throws RemoteException; }
UserManagerImpl.java:
package com.rmidemo.chenjun.impl; import java.rmi.RemoteException; import com.rmidemo.chenjun.bean.Account; import com.rmidemo.chenjun.stub.UserManagerInterface; public class UserManagerImpl implements UserManagerInterface { @Override public String getUserName() throws RemoteException { return "uname"; } @Override public Account getAdminAccount() throws RemoteException { Account account = new Account(); account.setUsername("admin"); account.setPassword("admin"); return account; } }
主类:ServerMain.java
package com.rmidemo.chenjun.app; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import com.rmidemo.chenjun.impl.UserManagerImpl; import com.rmidemo.chenjun.stub.UserManagerInterface; public class Main { public static void main(String[] args) throws RemoteException { UserManagerImpl userManager = new UserManagerImpl(); UserManagerInterface userManagerInterface = (UserManagerInterface) UnicastRemoteObject.exportObject(userManager, 0); Registry registry = LocateRegistry.createRegistry(2001); registry.rebind("userManager", userManagerInterface); System.out.println("server is ready"); } }
ClientMain.java | 客户端主类 |
直接贴代码:
package com.chenjun.rmiclient; import java.rmi.AccessException; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import com.rmidemo.chenjun.stub.UserManagerInterface; public class ClientApp { public static void main(String[] args) throws AccessException, RemoteException, NotBoundException { Registry registry = LocateRegistry.getRegistry("localhost", 2001); UserManagerInterface userManagerInterface = (UserManagerInterface) registry.lookup("userManager"); System.out.println("" + userManagerInterface.getAdminAccount().getUsername() + userManagerInterface.getAdminAccount().getPassword()); } }
运行以前,把服务器端java工程右键->export
而后
打成jar包,给客户端引用
如图,在客户端工程中引用这个打好的jar包:
依次启动服务器端、客户端,如图