JAVA RMI技術及其spring封裝的使用
java RMI即java遠端服務呼叫,用於各個子系統之間的服務呼叫。最近應用到了JAVA RMI技術,所以總結一下RMI的使用,另外EJB的實現也是以Java RMI為基礎核心的。RMI遠端方法呼叫,可以跨機器通過網路呼叫,不過Java RMI只支援兩邊都是Java程式,如果要實現完全兩邊異構,那麼就要用到傳說中的Web Service了。為了看好效果,都要建立兩個或兩個以上工程,當然,如果你有兩個機器並通過區域網相連更好,如果能同有網際網路的條件就更好了,以下是同一機器不同工程的實驗。
Java RMI
首先新建一個工程,隨便什麼工程,為了方便,就Java Project吧。 1、建立一個介面,繼承Remote
package leon.rmi.iface;import java.rmi.Remote;import java.rmi.RemoteException;/*** 定義遠端介面,必須繼承Remote介面,* 其中所有需要遠端呼叫的方法都必須丟擲RemoteException異常 */ public interface IHello extends Remote { public String sayHello(String name) throws RemoteException; public int sum(int a, int b)throws RemoteException;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2、建立介面的實現類
package leon.rmi.impl;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;import leon.rmi.iface.IHello;public class HelloImpl extends UnicastRemoteObject implements IHello { private static final long serialVersionUID = 1L; public HelloImpl() throws RemoteException { super(); } @Override public String sayHello(String name) throws RemoteException { return "Welcome, " + name; } @Override public int sum(int a, int b) throws RemoteException{ return a + b; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
說明:介面的實現類同時要實現 Serializable介面,這裡繼承UnicastRemoteObject也是間接實現Serializable介面,同時,因為構造方法需要丟擲 RemoteException,所以不能預設使用隱含的無參構造方法,而應該自己顯式定義構造方法。
3、建立應用類,註冊和啟動服務端RMI,以被客戶端呼叫
package leon.rmi.impl;import java.net.MalformedURLException;import java.rmi.AlreadyBoundException;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import leon.rmi.iface.IHello;public class HelloServer { public static void main(String args[]) { try { //建立一個遠端物件 IHello rhello = new HelloImpl(); //生成遠端物件登錄檔Registry的例項,並指定埠為8888(預設埠是1099) LocateRegistry.createRegistry(8888); //把遠端物件註冊到RMI註冊伺服器上,並命名為RHello //繫結的URL標準格式為:rmi://host:port/name(協議名可以省略,下面兩種寫法都可以) Naming.bind("rmi://10.225.112.86:8888/RHello", rhello); //Naming.bind("//10.225.112.86:8888/RHello",rhello); System.out.println(">>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(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
說明:繫結的地址10.225.112.86是我的區域網地址可以在DOS命令列用ipconfig檢視,如果你的機器沒有任何聯網,可以使用127.0.0.1或localhost。 執行HelloServer.java看到,紅色方塊顯示正在執行:>>INFO:遠端IHello物件繫結成功! 好了,現在遠端服務提供端建立完成,下面建立客戶端。 新建一個新的工程,為了方便,也是Java Project吧, 1、 因為客戶端需要有服務端那邊提供的介面,才可以訪問,所以要將服務端的IHello介面完全拷貝(連同包)到客戶端,當然為了方便,你在客戶端工程中新建一個完全一樣的介面也可以。實際運用中通常是要服務端介面打成jar包來提供的。 2、 建立客戶端呼叫類
package testrmi;import java.rmi.Naming;import leon.rmi.iface.IHello;public class HelloClient { public static void main(String args[]) { try { // 在RMI服務登錄檔中查詢名稱為RHello的物件,並呼叫其上的方法 IHello rhello = (IHello) Naming.lookup("rmi://10.225.112.86:8888/RHello"); System.out.println(rhello.sayHello("水哥")); System.out.println(rhello.sum(454, 5457)); } catch (Exception e) { e.printStackTrace(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
執行,顯示,成功。 呵呵,是不是很簡單?對吧。 下面我們要使用spring封裝的Java RMI技術,也是很多專案都會用到的。下面我有個Spring RMI的例子。
Spring RMI
Spring RMI中,主要有兩個類:org.springframework.remoting.rmi.RmiServiceExporter和org.springframework.remoting.rmi.RmiProxyFactoryBean 服務端使用RmiServiceExporter暴露RMI遠端方法,客戶端用RmiProxyFactoryBean間接呼叫遠端方法。
首先,也是兩個工程,服務端用Web工程,因為使用Spring,我們依託Web容器來完成。 1、在該服務端Web工程中新增介面,普通介面,無需繼承其他
package leon.rmi.iface;public interface IUserDao { public String getUserList(); public int sum(int a, int b);}
- 1
- 2
- 3
- 4
- 5
- 6
2、介面的實現類
package leon.rmi.impl;import leon.rmi.iface.IUserDao;public class UserDaoImpl implements IUserDao { @Override public String getUserList() { return "Hello,Get the user list from database!"; } @Override public int sum(int a, int b) { return a+b; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
3、在該服務端Web工程中新增Spring的bean配置檔案,比如命名為rmi.xml,內容如下:
<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <beans:bean id="userDaoRmi" class="leon.rmi.impl.UserDaoImpl"> </beans:bean> <beans:bean id="userSvcExporter" class="org.springframework.remoting.rmi.RmiServiceExporter"> <beans:property name="service" ref="userDaoRmi"/> <beans:property name="serviceName" value="userDaoService"/> <beans:property name="serviceInterface" value="leon.rmi.iface.IUserDao"/> <beans:property name="registryPort" value="9111"/> <beans:property name="servicePort" value="10023"/> </beans:bean> </beans:beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
說明:這裡不詳細的說明了,主要配置了真實實現類,用RmiServiceExporter暴露時,配置property要注意的有service,serviceName,serviceInterface,埠registryPort。 啟動Web工程的伺服器,該配置檔案應該被Spring的監聽器監聽,並載入,啟動成功後,服務端就算建好了。如果伺服器是在localhost啟動的,那麼暴露的RMI的IP也是localhost,如果需要使用其他IP,需要讓伺服器在其他的IP啟動。 客戶端呼叫 為了方便也只新建一個簡單的Java Project,使用靜態的java程式碼來呼叫了。 1、 在原始檔src下建立一個springbeans.xml
<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <beans:bean id="userDaoProxy" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <beans:property name="serviceUrl" value="rmi://localhost:9111/userDaoService"/> <beans:property name="serviceInterface" value="leon.rmi.iface.IUserDao"/> </beans:bean></beans:beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
這裡注意到RmiProxyFactoryBean的兩個重要的引數:serviceUrl和serviceInterface,IUserDao介面可以從服務端的介面打成jar包來提供。
2、 新建java類
package testrmi;import leon.rmi.iface.IUserDao;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestRMI2 { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("springbeans.xml"); IUserDao userDao = (IUserDao) ctx.getBean("userDaoProxy"); System.out.println(userDao.getUserList()); System.out.println(userDao.sum(145, 487)); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
執行,成功。 好了,這就是一個採用Spring封裝的RMI的例子,專案工作中應該經常使用的。
上面的spring配置都使用了<beans:bean><beans:property ... /></beans:bean>
這樣的標籤配置bean和引數, 是因為xmlns="http://www.springframework.org/schema/security"
這個配置指明瞭這個檔案的預設schema為security, 所以裡面的beans定義就需要加上字首beans,現在基本都這麼配:xmlns="http://www.springframework.org/schema/beans"
, 這樣以來直接使用<bean><property ... /></bean>
就可以了,更加方便。