JAVA基础--分布式对象

一、概念

  1. 当客户代码要在远程对象上调用一个远程方法时,实际上调用的是代理对象上的一个普通方法,我们称此代理对象为存根。存根在客户端上,而不是在服务器上。存根构造了一个信息块:远程对象的标识符、方法的描述、编组后的参数。

  2. 参数编组:把远程方法所需的参数打包成一组字节。目的是将参数转换成合适在虚拟机之间进行传递的格式。在RMI中,对象是使用序列化机制进行编码的。

  3. 客户端存根对来自服务端的返回值或者异常进行反编组,就成了调用存根的返回值。


二、RMI

1.创建远程方法接口,该接口必须继承自Remote接口,Remote 接口是一个标识接口,用于标识所包含的方法可以从非本地虚拟机上调用的接口,Remote接口本身不包含任何方法

package server;  
  
import java.rmi.Remote;  
import java.rmi.RemoteException;  
  
public interface Hello extends Remote {  
    public String sayHello(String name) throws RemoteException;  
}

注意:1)远程对象必须实现Remote接口。

    2)接口中的所有方法必须抛出RemoteException。


2.创建远程方法实现类

package server;  
  
import java.rmi.RemoteException;  
import java.rmi.server.UnicastRemoteObject;  
  
public class HelloImpl extends UnicastRemoteObject implements Hello {  

    private static final long serialVersionUID = -271947229644133464L;  
  
    public HelloImpl() throws RemoteException{  
        super();  
    }  
  
    public String sayHello(String name) throws RemoteException {  
        return "Hello,"+name;  
    }  
}

注意:1)UnicastRemoteObject类的构造函数抛出了RemoteException,这个构造器使得它的对象可供远程访问,因为其超类构造器也会抛出这个异常,故其继承类不能使用默认构造函数,继承类的构造函数必须也抛出RemoteException。

    2)由于方法参数与返回值最终都将在网络上传输,故必须是可序列化的


3,利用java自带rmic工具生成sutb存根类(jdk1.5.0_15/bin/rmic)

jdk1.2以后的RMI可以通过反射API可以直接将请求发送给真实类,所以不需要skeleton类了

sutb存根为远程方法类在本地的代理,是在服务端代码的基础上生成的,需要HelloImpl.class文件,由于HelloImpl继承了Hello接口,故Hello.class文件也是不可少的

Test

- - server

- - - - Hello.class

- - - - HelloImpl.class

方式一: view plain copy

  1. [name@name Test]$ cd /home/name/Test/  

  2. [name@name Test]$ rmic server.HelloImpl  

方式二:view plain copy

  1. [name@name Test]$ rmic -classpath /home/name/Test server.HelloImpl  

 运行成功后将会生成HelloImpl_Stub.class文件


4.启动RMI注册服务(jdk1.5.0_15/bin/rmiregistry)

方式一:后台启动rmiregistry服务view plain copy

  1. [name@name jdk]$ jdk1.5.0_15/bin/rmiregistry 12312 &  

  2. [1] 22720  

  3. [name@name jdk]$ ps -ef|grep rmiregistry  

  4. name    22720 13763  0 16:43 pts/3    00:00:00 jdk1.5.0_15/bin/rmiregistry 12312  

  5. name    22737 13763  0 16:43 pts/3    00:00:00 grep rmiregistry  

如果不带具体端口号,则默认为1099


5.服务端代码

package server;  
  
import java.rmi.Naming;  
import java.rmi.registry.LocateRegistry;  
  
public class HelloServer {  
    public static void main(String[] args) {  
        try{  
            Hello h = new HelloImpl();  
              
            //创建并导出接受指定port请求的本地主机上的Registry实例。  
            //LocateRegistry.createRegistry(12312);  
              
            /** Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法 
             *  Naming 类的每个方法都可将某个名称作为其一个参数, 
             *  该名称是使用以下形式的 URL 格式(没有 scheme 组件)的 java.lang.String: 
             *  //host:port/name 
             *  host:注册表所在的主机(远程或本地),省略则默认为本地主机 
             *  port:是注册表接受调用的端口号,省略则默认为1099,RMI注册表registry使用的著名端口 
             *  name:是未经注册表解释的简单字符串 
             */  
            //Naming.bind("//host:port/name", h);  
            Naming.bind("rmi://192.168.58.164:12312/Hello", h);  
            System.out.println("HelloServer启动成功");  
        }catch(Exception e){  
            e.printStackTrace();  
        }  
    }  
}


6.运行服务端(58.164):

Test

- - server

- - - - Hello.class

- - - - HelloImpl.class

- - - - HelloServer.class view plain copy

  1. [name@name ~]$ java server.HelloServer  

  2. HelloServer启动成功  

当然/home/name/Test一定要在系统CLASSPATH中,否则会报找不到相应的.class文件


7.编写客户端代码

package client;  
  
import java.net.MalformedURLException;  
import java.rmi.Naming;  
import java.rmi.NotBoundException;  
import java.rmi.RemoteException;  
  
import server.Hello;  
  
public class HelloClient {  
    public static void main(String[] args) {  
        try {  
            Hello h = (Hello)Naming.lookup("rmi://192.168.58.164:12312/Hello");  
            System.out.println(h.sayHello("zx"));  
        } catch (MalformedURLException e) {  
            System.out.println("url格式异常");  
        } catch (RemoteException e) {  
            System.out.println("创建对象异常");  
            e.printStackTrace();  
        } catch (NotBoundException e) {  
            System.out.println("对象未绑定");  
        }  
    }  
}


8.运行客户端(58.163):

Test

- - client

- - - - HelloClient.class

- - server

- - - - Hello.class

- - - - HelloImpl_Stub.class//服务端生成的存根文件 copy

  1. [name@name client]$ java client.HelloClient  

  2. Hello,zx  

同服务器端,/home/name/Test一定要在系统CLASSPATH中




spring中的RMI

Spring RMI中,主要有两个类:org.springframework.remoting.rmi.RmiServiceExporterorg.springframework.remoting.rmi.RmiProxyFactoryBean

服务端使用RmiServiceExporter暴露RMI远程方法,客户端用RmiProxyFactoryBean间接调用远程方法。

 

       首先,也是两个工程,服务端用Web工程,因为使用Spring,我们依托Web容器来完成。

       1、在该服务端Web工程中添加接口,普通接口,无需继承其他view plain copy

  1. package rmi.test;  

  2.   

  3. public interface HelloRMIService {  

  4.       

  5.     public int  getAdd(int a, int b);  

  6.        

  7.   

  8. }  


 

        2、接口的实现类view plain copy

  1. package rmi.test;  

  2.   

  3. public class HelloRMIServiceImpl implements HelloRMIService {  

  4.   

  5.     @Override  

  6.     public int getAdd(int a, int b) {  

  7.           

  8.         return a+b;  

  9.     }  

  10.   

  11. }  


 

       3、在该服务端Web工程中添加Spring的bean配置文件,比如命名为rmiServer.xml,内容如下: view plain copy

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:aop="http://www.springframework.org/schema/aop"  

  4.        xmlns:context="http://www.springframework.org/schema/context"  

  5.        xmlns:jee="http://www.springframework.org/schema/jee"   

  6.        xmlns:tx="http://www.springframework.org/schema/tx"  

  7.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  8.        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd     

  9.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     

  10.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd     

  11.        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd     

  12.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  

  13.   

  14.     <bean id="helloRMIServiceImpl" class="rmi.test.HelloRMIServiceImpl"> </bean>  

  15.     <!-- 将一个类发布为一个RMI服务 -->  

  16.     <bean id="myRMIServer"  class="org.springframework.remoting.rmi.RmiServiceExporter">  

  17.       <property name="serviceName" value="helloRMI"></property>  

  18.       <property name="service" ref="helloRMIServiceImpl"></property>  

  19.       <property name="serviceInterface" value="rmi.test.HelloRMIService"></property>  

  20.       <property name="registryPort" value="9999"></property>  

  21.     </bean>  

  22.       

  23.     

  24.       

  25. </beans>  


       说明:这里不详细的说明了,主要配置了真实实现类,用RmiServiceExporter暴露时,配置property要注意的有service,serviceName,serviceInterface,端口registryPort。

       启动Web工程的服务器,该配置文件应该被Spring的监听器监听,并加载,启动成功后,服务端就算建好了。如果服务器是在localhost启动的,那么暴露的RMIIP也是localhost,如果需要使用其他IP,需要让服务器在其他的IP启动。

    这里没有web服务器,使用main模拟启动,如下:

[html] view plain copy

  1. package rmi.test;  

  2.   

  3. import org.springframework.context.support.ClassPathXmlApplicationContext;  

  4.   

  5. public class RMIServiceTest {  

  6.       

  7.     public static void main(String[] args) {  

  8.           

  9.         new ClassPathXmlApplicationContext("rmiServer.xml");  

  10.           

  11.     }  

  12.   

  13. }  


     客户端调用:为了方便也只新建一个简单的Java Project,使用静态的java代码来调用了。

     1、 在源文件src下建立一个rmiClient.xml view plain copy

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:aop="http://www.springframework.org/schema/aop"  

  4.        xmlns:context="http://www.springframework.org/schema/context"  

  5.        xmlns:jee="http://www.springframework.org/schema/jee"   

  6.        xmlns:tx="http://www.springframework.org/schema/tx"  

  7.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  8.        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd     

  9.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     

  10.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd     

  11.        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd     

  12.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  

  13.   

  14.     <!-- 客户端 -->  

  15.     <bean id="myRMIClient"  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">  

  16.       <property name="serviceInterface" value="rmi.test.HelloRMIService"></property>  

  17.       <property name="serviceUrl" value="rmi://127.0.0.1:9999/helloRMI"></property>  

  18.     </bean>  

  19.       

  20.          

  21. </beans>  

 

       这里注意到RmiProxyFactoryBean的两个重要的property:serviceUrlserviceInterface,HelloRMIService接口可以从服务端的接口打成jar包来提供。

        2、 新建java类 view plain copy

  1. package rmi.test;  

  2.   

  3. import org.springframework.context.ApplicationContext;  

  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  

  5.   

  6. public class RMIClient {  

  7.       

  8.     public static void main(String[] args) {  

  9.         ApplicationContext applicationContext = new ClassPathXmlApplica                    tionContext("rmi/test/rmiClient.xml");  

  10.         HelloRMIService helloRMIService =  applicationContext.getBean("                myRMIClient",HelloRMIService.class);  

  11.         System.out.println(helloRMIService.getAdd(3, 4));  

  12.     }  

  13.   

  14. }  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章