Java RMI详解

转自:

Java RMI详解

下文笔者讲述RMI详解说明,如下所示

Java RMI简介说明

 Java远程方法调用
  即Java RMI(Java Remote Method Invocation)是Java编程语言里
  一种用于实现远程过程调用的应用程序编程接口

当客户机上运行的程序可以调用远程服务器上的对象
  远程方法调用特性使Java编程人员能够在网络环境中分布操作
  RMI的功能:
    简化远程接口对象的使用
    RMI远程方法调用:可使用虚拟机对象调用运行另一个虚拟机上对象的方法

JavaRMI包含内容

1、远程服务的接口定义
2、远程服务接口的具体实现
3、桩(Stub)和框架(Skeleton)文件
4、一个运行远程服务的服务器
5、一个RMI命名服务,它允许客户端去发现这个远程服务
6、类文件的提供者(一个HTTP或者FTP服务器)
7、一个需要这个远程服务的客户端程序

采用以上信息可实现RMI的远程调用

RMI的用途

为分布式Java应用程序之间的远程通信提供服务
  提供分布式服务

例:
在Spring中实现RMI:

 1.在服务器端定义服务的接口,定义特定的类实现这些接口
 2.在服务器端使用org.springframework.remoting.rmi.RmiServiceExporter类来注册服务
 3.在客户端使用org.springframework.remoting.rmi.RmiProxyFactoryBean来实现远程服务的代理功能
 4.在客户端定义访问与服务器端服务接口相同的类

RMI局限性

RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信
 JRMP是专为Java的远程对象制定的协议
  由于JRMP是专为Java对象制定的
  因此 RMI对于用非Java语言开发的应用系统的支持不足

不能与用非Java语言书写的对象进行通信(只支持客户端和服务器端都是Java程序的代码的远程调用)

RMI调用远程方法的参数和返回值

当调用远程对象上的方法时
  客户机除了可以将原始类型的数据作为参数一外
   还可以将对象作为参数来传递
   与之相对应的是返回值
  可以返回原始类型或对象
   这些都是通过Java的对象序列化(serialization)技术来实现
  

RMI体系结构

桩/框架(Stub/Skeleton)层:客户端的桩和服务器端的框架;

远程引用(remote reference)层:处理远程引用行为

传送层(transport):连接的建立和管理,以及远程对象的跟踪

RMI类和接口

(一) Remote接口:是一个不定义方法的标记接口

Public interface Remote{}

在RMI中,远程接口声明了可以从远程Java虚拟机中调用的方法集。远程接口满足下列要求:

1、远程接口必须直接或间接扩展Java.rmi.Remote接口,且必须声明为public,除非客户端于远程接口在同一包中

2、在远程接口中的方法在声明时,除了要抛出与应用程序有关的一场之外,还必须包括RemoteException(或它的超类,IOExcepion或Exception)异常

3、在远程方法声明中,作为参数或返回值声明的远程对象必须声明为远程接口,而非该接口的实现类。

(二) RemoteObject抽象类实现了Remote接口和序列化Serializable接口,它和它的子类提供RMI服务器函数。

(三) LocateRegistry final()类用于获得特定主机的引导远程对象注册服务器程序的引用(即创建stub),或者创建能在特定端口接收调用的远程对象注册服务程序。

服务器端:向其他客户机提供远程对象服务

SomeService servcie=……;//远程对象服务
只听到从山间传来架构君的声音:
轻言托朋友,对面九疑峰。有谁来对上联或下联?
1、Registry registry=LocateRegisty.getRegistry();//Registry是个接口,他继承了Remote,此方法返回本地主机在默认注册表端口 1099 上对远程对象 Registry 的引用。

2、getRegistry(int port) 返回本地主机在指定 port 上对远程对象 Registry 的引用;


3、getRegistry(String host) 返回指定 host 在默认注册表端口 1099 上对远程对象 Registry 的引用;

4、getRegistry(String host, int port) 返回指定的 host 和 port 上对远程对象 Registry 的引用

5、registry.bind(“I serve”,service);// bind(String name,Remote obj) 绑定对此注册表中指定 name 的远程引用。name : 与该远程引用相关的名称 obj : 对远程对象(通常是一个 stub)的引用

6、unbind(String name)移除注册表中指定name的绑定。

7、rebind(String name,Remote obj)重新绑定,如果name已存在,但是Remote不一样则替换,如果Remote一样则丢弃现有的绑定

8、lookup(String name) 返回注册表中绑定到指定 name 的远程引用,返回Remote


9、String[] list() 返回在此注册表中绑定的名称的数组。该数组将包含一个此注册表中调用此方法时绑定的名称快照。

客户机端:向服务器提供相应的服务请求。

Registry registry=LocateRegisty.getRegistry();
SomeService servcie=(SomeService)registry.lookup(“I serve”);
Servcie.requestService();

(四) Naming类和Registry类类似。

客户端:

此代码由Java架构师必看网-架构君整理
Naming.lookup(String url)
url 格式如下"rmi://localhost/"+远程对象引用
服务器端:

Registry registry=LocateRegistry.createRegistry(int port);
Naming.rebind(“service”,service);
(五) RMISecurityManager类

在RMI引用程序中,如果没有设置安全管理器,则只能从本地类路径加载stub和类,这可以确保应用程序不受由远程方法调用所下载的代码侵害

在从远程主机下载代码之前必须执行以下代码来安装RMISecurityManager:

System.setSecurityManager(new RMISecurityManager());

例:

 远程服务的接口定义
UserManagerInterface.java
 
package com.java265;
 
import java.rmi.Remote;
import java.rmi.RemoteException;
 
import com.java265.rmiserver.bean.Account;
 
public interface UserManagerInterface extends Remote{
    public String getUserName() throws RemoteException;
    public Account getAdminAccount() throws RemoteException;
}
接口必须继承Remote类,每一个定义地方法都要抛出RemoteException异常对象。

2. 接口的具体实现 
UserManagerImp.java

package com.java265.rmiserver;
 
import java.rmi.RemoteException;
 
import com.java265.UserManagerInterface;
import com.java265.rmiserver.bean.Account;
 
public class UserManagerImp implements UserManagerInterface {
    public UserManagerImp() throws RemoteException {
 
    }
    private static final long serialVersionUID = -3111492742628447261L;
 
    public String getUserName() throws RemoteException{
        return "java265";
    }
    public Account getAdminAccount() throws RemoteException{
        Account account=new Account();
        account.setUsername("java265");
        account.setPassword("8888888888");
        return account;
    }
}
3. 定义一个bean
    实现implements Serializable序列化接口
	  也就是可以在client和server端进行传输的可序列化对象。
Account.java

package com.java265.rmiserver.bean;
 
import java.io.Serializable;
 
public class Account implements Serializable,Cloneable{
    private static final long serialVersionUID = -9988322323L;
    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;
    }
}

4. 定义server端的主程序入口
Entry.java
package com.java265.rmiserver.entry;
 
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
 
import com.java265.rmiserver.UserManagerImp;
import com.java265.UserManagerInterface;
 
public class Entry {
    public static void main(String []args) throws AlreadyBoundException, RemoteException{
        UserManagerImp userManager=new UserManagerImp();
        UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0);
        // Bind the remote object's stub in the registry
        Registry registry = LocateRegistry.createRegistry(2002);
       
        registry.rebind("userManager", userManagerI);
        System.out.println("server is ready");
        }
}
(四) client端代码
1、把Server端的Account类和接口UserManagerInterface 导出Export成jar包,命名为:RmiServerInterface.jar。导入到client中。
2、项目——右键——Export——java——jar file——next——选择Account类和接口UserManagerInterface——命名为:RmiServerInterface.jar如下图:

3. 新建一个java Project,导入jar包,编写客户端代码。

ClientEntry.java

package weiblog.rmi;
 
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
 
import com.java265.UserManagerInterface;
 
public class ClientEntry {
    
    public static void main(String []args){
        
        try {
            Registry registry = LocateRegistry.getRegistry("localhost",2004);
            UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager");
            System.out.println("用户名是"+userManager.getAdminAccount().getUsername()
                    +"密码"+userManager.getAdminAccount().getPassword());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NotBoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
 
}
5. 先运行服务器端代码
   然后运行客户端代码
     就会显示运行结果
	 客户端可以运行多次
	 每次都可以取得服务器端的对象
	 如果要再次运行客户端代码就需要更改端口号
	 如果不更改就会显示端口号被占用

你可能感兴趣的:(java,java)