RMI在java中是一个古老的程序协作方式,允许一个JVM中运行的程序去调用另外一个JVM中运行的程序,通常是跨机器的方法调用。实现这种技术需要使用到代理,确切的说是
远程代理,在旧版的java中,在远程服务方需要生成skeleton对象,在调用方需要生成stub对象,调用的过程是调用方请求stub对象,stub对象与远程的skeleton通信,skeleton对
象请求服务,将结果通过网络传回给stub对象,stub对象再返回给调用方。
在JDK中提供一套基础框架(java.rmi.*),用于实现RMI。
下面给出在JDK1.8中的RMI使用示例。
1. 定义远程接口
单独新建一个项目,该项目中只包含Model和Interface
1. 由于Model对象在网络上传输,因此Model实体类必须实现Serializable接口。
Model类student定义
package org.rmi.api.model;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Student implements Serializable {
private String id;
private String name;
private String no;
private int age;
private Sex sex;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Sex getSex() {
return sex;
}
public void setSex(Sex sex) {
this.sex = sex;
}
@Override
public int hashCode(){
return id.hashCode() & name.hashCode();
}
@Override
public boolean equals(Object obj){
if(obj==null){
return false;
}
if(obj instanceof Student){
Student stu = (Student)obj;
return stu.id.equals(this.id) && stu.name.equals(this.name);
}else{
return false;
}
}
}
Sex枚举
package org.rmi.api.model;
public enum Sex {
MALE(1){
public String getName(){
return "男";
}
public int getCode(){
return 1;
}
},FEMALE(0){
public String getName(){
return "女";
}
public int getCode(){
return 0;
}
};
private Sex(int i){
this.index=i;
}
public int getIndex() {
return index;
}
private final int index;
public abstract String getName();
public abstract int getCode();
}
2. Interface扩展Remote接口,表示这是一个远程接口,同时所有的接口方法抛出RemoteException,这是由于客户端会采用面向接口的方式,直接调用接口的方法,
客户端调用时可能会由于网络原因抛出异常。
为了简单起见,只定义一个接口package org.rmi.api.service;
import java.rmi.Remote;
import java.rmi.RemoteException;
import org.rmi.api.model.Student;
public interface StudentService extends Remote {
Student getStudentById(String id) throws RemoteException;
boolean addStudent(Student stu) throws RemoteException;
boolean deleteStudent(Student stu) throws RemoteException;
}
单独新建一个项目,该项目中引入接口包,该项目可以独立部署。
1. 定义实现类,实现接口对应的方法,这些实现可以具备一些复杂的功能,如读写文件,修改数据库等。
该类必须扩展UnicastRemoteObject类,该类具备远程代理的功能:暴露一个远程对象,并拥有一个stub,具备有远程调用方通信的功能。在扩展该类时,会要求创建一个抛出RemoteException异常的构造器,否则编译无法通过(UnicastRemoteObject的构造器抛RemoteException)。
package org.rmi.service.provider.service;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashSet;
import java.util.Set;
import org.rmi.api.model.Student;
import org.rmi.api.service.StudentService;
@SuppressWarnings("serial")
public class StudentServiceRemoteImpl extends UnicastRemoteObject implements StudentService {
private static final Set students = new HashSet();
public StudentServiceRemoteImpl() throws RemoteException {
super();
}
public Student getStudentById(String id) throws RemoteException {
System.out.println("Remoted Service: getStudentById invoked, and get parmater:"+id);
for(Student stu : students){
if(stu.getId().equals(id)){
return stu;
}
}
return null;
}
public boolean addStudent(Student stu) throws RemoteException {
System.out.println("Remoted Service: addStudent invoked, and get parmater:"+stu);
return students.add(stu);
}
public boolean deleteStudent(Student stu) throws RemoteException {
System.out.println("Remoted Service: deleteStudent invoked, and get parmater:"+stu);
return students.remove(stu);
}
}
2. 远程实现需要注册到RMI注册中心上。
简单起见,可以运行一个main方法,将接口实现绑定到注册中心
2.1. 代码中创建registry,然后bind
2.2. 代码中直接调用Naming.rebind方法,需要在程序之外启动rmiregistry。package org.rmi.service.provider;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.PriorityQueue;
import org.rmi.api.service.StudentService;
import org.rmi.service.provider.service.StudentServiceRemoteImpl;
/**
* Hello world!
*
*/
public class StartRemoteServiceApplication
{
public static void main( String[] args )
{
//registerRemotViaRegistry();
registerRemotViaNaming();
}
private static void registerRemotViaRegistry(){
try {
StudentService studentService = new StudentServiceRemoteImpl();
Registry registry = LocateRegistry.createRegistry(8088);
registry.bind("StudentService", studentService);
System.out.println("Server startup!");
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
private static void registerRemotViaNaming(){
try {
StudentService studentService = new StudentServiceRemoteImpl();
Naming.rebind("rmi://127.0.0.1:8088/StudentService", studentService);
System.out.println("Server startup!");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
3. 远程调用
单独新建一个项目,该项目中引入接口包,该项目可以独立部署。
通过Naming.lookup(package org.rmi.service.consumer;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import org.rmi.api.model.Sex;
import org.rmi.api.model.Student;
import org.rmi.api.service.StudentService;
/**
* Hello world!
*
*/
public class ConsumerRemoteServiceApplication
{
public static void main( String[] args )
{
try {
StudentService remoteStudentService = (StudentService) Naming.lookup("rmi://127.0.0.1:8088/StudentService");
boolean addResult = addStudent(remoteStudentService);
System.out.println("add student " + (addResult ? "success" : "failed"));
Student stu1 = getStudentById(remoteStudentService,"001");
System.out.println("get student " + (stu1!=null ? "success: "+stu1 : "failed"));
boolean deleteResult = deleteStudent(remoteStudentService);
System.out.println("delete student " + (deleteResult ? "success" : "failed"));
Student stu2 = getStudentById(remoteStudentService,"001");
System.out.println("get student " + (stu2!=null ? "success: "+stu2 : "failed"));
System.in.read();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static boolean addStudent(StudentService remoteStudentService){
Student stu = new Student();
stu.setId("001");
stu.setName("peter");
stu.setAge(18);
stu.setNo("05061059");
stu.setSex(Sex.MALE);
System.out.println("Consumer try to add Student: " + stu);
try{
return remoteStudentService.addStudent(stu);
}catch(RemoteException e){
e.printStackTrace();
}
return false;
}
private static Student getStudentById(StudentService remoteStudentService, String id){
try{
System.out.println("Consumer try to get Student: " + id);
return remoteStudentService.getStudentById(id);
}catch(RemoteException e){
e.printStackTrace();
}
return null;
}
private static boolean deleteStudent(StudentService remoteStudentService){
Student stu = new Student();
stu.setId("001");
stu.setName("peter");
stu.setAge(18);
stu.setNo("05061059");
stu.setSex(Sex.MALE);
System.out.println("Consumer try to delete Student: " + stu);
try{
return remoteStudentService.deleteStudent(stu);
}catch(RemoteException e){
e.printStackTrace();
}
return false;
}
}
1. 在程序外部启动rmiregistry的方式,注意远程服务StartRemoteServiceApplication中main方法调用的的方法为registerRemotViaNaming();
a)以windows平台为例,打开cmd,进入接口工程的src/main/java目录, 运行 rmiregistry 8088,如果不在该目录下运行,在启动远程服务时会报找不到接口b)启动远程服务
在eclipse中运行rmi-service-provider项目的主类StartRemoteServiceApplication
c) 启动调用方
在eclipse中运行rmi-service-consumer项目的主类ConsumerRemoteServiceApplication
在远程服务中的输出
2. 我们可以以内置registry的方式运行
a) ctrl+c 停止外部的rmiregistry
b) 然后修改远程服务中StartRemoteServiceApplication中的main方法,注释registerRemotViaNaming,改为调用registerRemotViaRegistry
在eclipse中运行rmi-service-provider项目的主类StartRemoteServiceApplication
c)类似的运行rmi-service-consumer项目的主类ConsumerRemoteServiceApplication
我们可以得到相同的输出。
5. 写在最后1. 远程服务和调用方通常运行在不同的机器上, 这时可以将调用方查找的地址写在配置文件中或通过环境变量的方式获取。
2. 源码下载地址: http://download.csdn.net/detail/musa875643dn/9825336
3. 这两种方式有什么不同?这个问题留给大家,^_^ 。
本地地址: http://blog.csdn.net/musa875643dn/article/details/71703069