1. 程式人生 > >Spring RMI實現遠端呼叫

Spring RMI實現遠端呼叫

1. Spring 除了使用基於 HTTP 協議的遠端呼叫方案,還為開發者提供了基於 RMI 機制的遠端呼叫方法, RMI 遠端呼叫網路通訊實現是基於 TCP/IP 協議完成的,而不是通過 HTTP 協議。

在 Spring RMI 實現中,集成了標準的 RMI-JRIM 解決方案,該方案是 java 虛擬機器實現的一部分,它使用 java 序列化來完成物件的傳輸,是一個 java 到 java 環境的分散式處理技術,不涉及異構平臺的處理。

2.RMI 客戶端配置:

和基於 HTTP 協議的遠端呼叫類似, RMI 遠端呼叫客戶端也需要進行類似如下的配置:

[java] view plain
copyprint?
  1. <bean id=”rmiProxy” class=”org.springframework.remoting.rmi.RmiProxyFactoryBean”>  
  2.     <property name=”serviceUrl”>  
  3.         <value>rmi://hostAddress:1099/serviceUrl</value>   
  4.     </property>  
  5.     <property name=”serviceInterface”>  
  6.         <value>遠端呼叫介面</value>  
  7.     </property>  
  8. </bean>  
  9. <bean id=”rmiClient” class=”RMI遠端呼叫客戶端類全路徑”>  
  10.     <property name=”serviceInterface”>  
  11.         <ref bean=”rmiProxy”/>  
  12.     </property>  
  13. </bean>  

注意:上面的配置中 serviceUrl 必須和服務端的遠端呼叫提供者的 id 一致,另外, serviceUrl 中使用的是 rmi 協議,預設埠是 1099.

RmiProxyFactoryBean 的主要功能是對 RMI 客戶端封裝,生成代理物件,查詢得到 RMI 的 stub 物件,並通過這個 stub 物件發起相應的 RMI 遠端呼叫請求。其原始碼如下:

[java] view plaincopyprint?
  1. public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean<Object>, BeanClassLoaderAware {  
  2.     //遠端呼叫代理物件   
  3.     private Object serviceProxy;  
  4. //Spring IoC容器完成依賴注入後的回撥方法   
  5.     public void afterPropertiesSet() {  
  6.         //呼叫父類RmiClientInterceptor的回撥方法   
  7.         super.afterPropertiesSet();  
  8.         //獲取客戶端配置的遠端呼叫介面   
  9.         if (getServiceInterface() == null) {  
  10.             throw new IllegalArgumentException("Property 'serviceInterface' is required");  
  11.         }  
  12.         //建立遠端呼叫代理物件併為代理物件設定攔截器。注意第二個引數this,因為   
  13.         //RmiProxyFactoryBean繼承RmiClientInterceptor,因此其也是攔截器   
  14.         this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());  
  15.     }  
  16.     //Spring IoC容器獲取被管理物件的介面方法,獲取遠端呼叫代理   
  17.     public Object getObject() {  
  18.         return this.serviceProxy;  
  19.     }  
  20.     public Class<?> getObjectType() {  
  21.         return getServiceInterface();  
  22.     }  
  23.     public boolean isSingleton() {  
  24.         return true;  
  25.     }  
  26. }  

通過對上面 RmiProxyFactoryBean 原始碼分析中,我們看到在建立遠端呼叫代理物件的時候需要設定攔截器,因為我們繼續分析遠端呼叫客戶端攔截器 RmiClientInterceptor

RmiClientInterceptor 對客戶端的遠端呼叫進行攔截,具體的生成遠端呼叫代理物件、查詢遠端呼叫 stub 、以及通過 RMI stub 向服務端發起遠端呼叫請求的具體實現都由 RMI 客戶端攔截器實現,其原始碼如下:

[java] view plaincopyprint?
  1. public class RmiClientInterceptor extends RemoteInvocationBasedAccessor  
  2.         implements MethodInterceptor {  
  3.     //在Spring啟動時查詢遠端呼叫stub   
  4.     private boolean lookupStubOnStartup = true;  
  5.     //對查詢到或使用過的遠端呼叫stub進行快取   
  6.     private boolean cacheStub = true;  
  7.     //當連線失敗是是否重新整理遠端呼叫stub   
  8.     private boolean refreshStubOnConnectFailure = false;  
  9.     //RMI客戶端socket工廠   
  10.     private RMIClientSocketFactory registryClientSocketFactory;  
  11.     //快取的遠端呼叫stub   
  12.     private Remote cachedStub;  
  13.     //建立遠端呼叫stub監控器   
  14.     private final Object stubMonitor = new Object();  
  15. //設定是否啟動時查詢RMI stub   
  16.     public void setLookupStubOnStartup(boolean lookupStubOnStartup) {  
  17.         this.lookupStubOnStartup = lookupStubOnStartup;  
  18.     }  
  19.     //設定是否快取以查詢的RMI stub   
  20.     public void setCacheStub(boolean cacheStub) {  
  21.         this.cacheStub = cacheStub;  
  22.     }  
  23.     //設定當連線失敗時,是否重新整理RMI stub   
  24.     public void setRefreshStubOnConnectFailure(boolean refreshStubOnConnectFailure) {  
  25.         this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;  
  26.     }     
  27.     //設定客戶端socket工廠   
  28.     public void setRegistryClientSocketFactory(RMIClientSocketFactory registryClientSocketFactory) {  
  29.         this.registryClientSocketFactory = registryClientSocketFactory;  
  30.     }  
  31. //Spring IoC容器回撥方法,由子類RmiProxyFactoryBean回撥方法呼叫   
  32.     public void afterPropertiesSet() {  
  33.         //呼叫父類RemoteInvocationBasedAccessor的回撥方法   
  34.         super.afterPropertiesSet();  
  35.         prepare();  
  36.     }  
  37.     //初始化RMI客戶端   
  38.     public void prepare() throws RemoteLookupFailureException {  
  39.         //如果設定了啟動時查詢RMI stub   
  40.         if (this.lookupStubOnStartup) {  
  41.             //查詢RMI stub   
  42.             Remote remoteObj = lookupStub();  
  43.             if (logger.isDebugEnabled()) {  
  44.                 //如果查詢到的RMI stub是RmiInvocationHandler型別   
  45.                 if (remoteObj instanceof RmiInvocationHandler) {  
  46.                     logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker");  
  47.                 }  
  48.                 //如果獲取到客戶端配置的serviceInterface不為null   
  49.                 else if (getServiceInterface() != null) {  
  50.                     //判斷客戶端配置的serviceInterface是否是RMI stub例項   
  51.                     boolean isImpl = getServiceInterface().isInstance(remoteObj);  
  52.                     logger.debug("Using service interface [" + getServiceInterface().getName() +  
  53.                         "] for RMI stub [" + getServiceUrl() + "] - " +  
  54.                         (!isImpl ? "not " : "") + "directly implemented");  
  55.                 }  
  56.             }  
  57.             //如果設定了快取RMI stub,將快取的stub設定為查詢到的RMI stub   
  58.             if (this.cacheStub) {  
  59.                 this.cachedStub = remoteObj;  
  60.             }  
  61.         }  
  62.     }  
  63.     //查詢RMI stub   
  64.     protected Remote lookupStub() throws RemoteLookupFailureException {  
  65.         try {  
  66.             Remote stub = null;  
  67.             //如果設定了客戶端socket工廠   
  68.             if (this.registryClientSocketFactory != null) {  
  69.                 //獲取並解析客戶端配置的serviceUrl   
  70.                 URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler());  
  71.                 //獲取客戶端配置的serviceUrl協議   
  72.                 String protocol = url.getProtocol();  
  73.                 //如果客戶端配置的serviceUrl中協議不為null且不是rmi   
  74.                 if (protocol != null && !"rmi".equals(protocol)) {  
  75.                     throw new MalformedURLException("Invalid URL scheme '" + protocol + "'");  
  76.                 }  
  77.                 //獲取客戶端配置的serviceUrl中的主機地址   
  78.                 String host = url.getHost();  
  79.                 //獲取客戶端配置的serviceUrl中的埠   
  80.                 int port = url.getPort();  
  81.                 //獲取客戶端配置的serviceUrl中請求路徑   
  82.                 String name = url.getPath();  
  83.                 //如果請求路徑不為null,且請求路徑以”/”開頭,則去掉”/”   
  84.                 if (name != null && name.startsWith("/")) {  
  85.                     name = name.substring(1);  
  86.                 }  
  87.                 //根據客戶端配置的serviceUrl資訊和客戶端socket工廠建立遠端對   
  88. //象引用   
  89.                 Registry registry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory);  
  90.                 //通過遠端物件引用查詢指定RMI請求的RMI stub   
  91.                 stub = registry.lookup(name);  
  92.             }  
  93.             //如果客戶端配置的serviceUrl中協議為null或者是rmi   
  94.             else {  
  95.                 //直接通過RMI標準API查詢客戶端配置的serviceUrl的RMI stub   
  96.                 stub = Naming.lookup(getServiceUrl());  
  97.             }  
  98.             if (logger.isDebugEnabled()) {  
  99.                 logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]");  
  100.             }  
  101.             return stub;  
  102.         }  
  103.         //對查詢RMI stub過程中異常處理   
  104.         catch (MalformedURLException ex) {  
  105.             throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);  
  106.         }  
  107.         catch (NotBoundException ex) {  
  108.             throw new RemoteLookupFailureException(  
  109.                     "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);  
  110.         }  
  111.         catch (RemoteException ex) {  
  112.             throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);  
  113.         }  
  114.     }  
  115.     //獲取RMI stub   
  116.     protected Remote getStub() throws RemoteLookupFailureException {  
  117.         //如果沒有配置快取RMI stub,或者設定了啟動時查詢RMI stub或當連線失敗時   
  118.         //不重新整理RMI stub   
  119.         if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) {  
  120.             //如果快取的RMI stub不為null,則直接返回,否則,查詢RMI stub   
  121.             return (this.cachedStub != null ? this.cachedStub : lookupStub());  
  122.         }  
  123.         //如果設定了快取RMI stub,且設定了啟動時查詢RMI stub或者當連線失敗時重新整理   
  124.         //RMI stub   
  125.         else {  
  126.             //執行緒同步   
  127.             synchronized (this.stubMonitor) {  
  128.                 //如果快取的RMI stub為null   
  129.                 if (this.cachedStub == null) {  
  130.                     //則將查詢的RMI stub快取   
  131.                     this.cachedStub = lookupStub();  
  132.                 }  
  133.                 //返回快取的RMI stub   
  134.                 return this.cachedStub;  
  135.             }  
  136.         }  
  137.     }  
  138.     //攔截器對客戶端遠端呼叫方法的攔截入口   
  139.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  140.         //獲取RMI stub   
  141.         Remote stub = getStub();  
  142.         try {  
  143.             //攔截客戶端遠端呼叫方法   
  144.             return doInvoke(invocation, stub);  
  145.         }  
  146.         catch (RemoteConnectFailureException ex) {  
  147.             return handleRemoteConnectFailure(invocation, ex);  
  148.         }  
  149.         catch (RemoteException ex) {  
  150.             if (isConnectFailure(ex)) {  
  151.                 return handleRemoteConnectFailure(invocation, ex);  
  152.             }  
  153.             else {  
  154.                 throw ex;  
  155.             }  
  156.         }  
  157.     }  
  158.     //判斷是否連線失敗   
  159.     protected boolean isConnectFailure(RemoteException ex) {  
  160.         return RmiClientInterceptorUtils.isConnectFailure(ex);  
  161.     }  
  162.     //處理遠端連線失敗   
  163.     private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable {  
  164.         //如果設定了當連線失敗時,重新整理RMI stub   
  165.         if (this.refreshStubOnConnectFailure) {  
  166.             String msg = "Could not connect to RMI service [" + getServiceUrl() + "] - retrying";  
  167.             if (logger.isDebugEnabled()) {  
  168.                 logger.warn(msg, ex);  
  169.             }  
  170.             else if (logger.isWarnEnabled()) {  
  171.                 logger.warn(msg);  
  172.             }  
  173.             //重新整理查詢遠端呼叫stub   
  174.             return refreshAndRetry(invocation);  
  175.         }  
  176.         else {  
  177.             throw ex;  
  178.         }  
  179.     }  
  180.     //重新整理RMI stub   
  181.     protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable {  
  182.         Remote freshStub = null;  
  183.         //執行緒同步   
  184.         synchronized (this.stubMonitor) {  
  185.             this.cachedStub = null;  
  186.             //查詢RMI stub   
  187.             freshStub = lookupStub();  
  188.             //如果設定了快取RMI stub   
  189.             if (this.cacheStub) {  
  190.                 //將重新整理查詢的RMI stub快取   
  191.                 this.cachedStub = freshStub;  
  192.             }  
  193.         }  
  194.         return doInvoke(invocation, freshStub);  
  195.     }  
  196.     //具體RMI呼叫的地方   
  197.     protected Object doInvoke(MethodInvocation invocation, Remote stub) throws Throwable {  
  198.         //如果RMI stub是RmiInvocationHandler型別   
  199.         if (stub instanceof RmiInvocationHandler) {  
  200.             //呼叫RmiInvocationHandler的RMI   
  201.             try {  
  202.                 return doInvoke(invocation, (RmiInvocationHandler) stub);  
  203.             }  
  204.             catch (RemoteException ex) {  
  205.                 throw RmiClientInterceptorUtils.convertRmiAccessException(  
  206.                     invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl());  
  207.             }  
  208.             catch (InvocationTargetException ex) {  
  209.                 Throwable exToThrow = ex.getTargetException();  
  210.                 RemoteInvocationUtils.fillInClientStackTraceIfPossible(exToThrow);  
  211.                 throw exToThrow;  
  212.             }  
  213.             catch (Throwable ex) {  
  214.                 throw new RemoteInvocationFailureException("Invocation of method [" + invocation.getMethod() +  
  215.                         "] failed in RMI service [" + getServiceUrl() + "]", ex);  
  216.             }  
  217.         }  
  218.         //如果RMI stub不是RmiInvocationHandler型別   
  219.         else {  
  220.             //使用傳統的RMI呼叫方式   
  221.             try {  
  222.                 return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, stub);  
  223.             }  
  224.             catch (InvocationTargetException ex) {  
  225.                 Throwable targetEx = ex.getTargetException();  
  226.                 if (targetEx instanceof RemoteException) {  
  227.                     RemoteException rex = (RemoteException) targetEx;  
  228.                     throw RmiClientInterceptorUtils.convertRmiAccessException(  
  229.                             invocation.getMethod(), rex, isConnectFailure(rex), getServiceUrl());  
  230.                 }  
  231.                 else {  
  232.                     throw targetEx;  
  233.                 }  
  234.             }  
  235.         }  
  236.     }  
  237.     //呼叫RmiInvocationHandler的RMI   
  238.     protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)  
  239.         throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {  
  240.         //如果客戶端遠端呼叫請求是toString()方法   
  241.         if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {  
  242.             return "RMI invoker proxy for service URL [" + getServiceUrl() + "]";  
  243.         }  
  244.         //使用RmiInvocationHandler處理RMI呼叫   
  245.         return invocationHandler.invoke(createRemoteInvocation(methodInvocation));  
  246.     }  
  247. }  

通過上面對 RmiClientInterceptor 原始碼分析,我們看到 Spring RMI 遠端呼叫使用以下兩種方式:

(1).RMI 呼叫器方式:

這種方式和 Spring HTTP 呼叫器非常類似,即使用 RemoteInvocation 來封裝呼叫目標物件、目標方法、引數型別等資訊, RMI 伺服器端接收到 RMI 請求之後直接呼叫目標物件的匹配方法。

(2). 傳統 RMI 呼叫方式:

使用 JDK 的反射機制,直接呼叫遠端呼叫 stub 的方法。

5.RMI 的服務端配置:

在 Spring RMI 服務端需要進行類似如下的配置:

[xhtml] view plaincopyprint?
  1. <bean id=”rmiService” class=”org.springframework.remoting.rmi.RmiServiceExporter”>  
  2. <property name=”service”>  
  3. <ref bean=”RMI服務端物件”/>  
  4. </property>  
  5. <property name=”serviceInterface”>  
  6. <value>RMI服務介面</value>  
  7. </property>  
  8. <property name=”serviceName”>  
  9. <value>RMI服務匯出名稱</value>  
  10. </property>  
  11. <property name=”registerPort”>  
  12. <value>1099</value>  
  13. </property>  
  14. </bean>  

RMI 中,基於 TCP/IP 協議,而不是 HTTP 協議來實現底層網路通訊,由於 RMI 的網路通訊已由 Java RMI 實現,所以這裡不再使用 Spring MVC 的 DispatcherServlet 來轉發客戶端配置的遠端呼叫請求 URL ,只需要指定 RMI 的 TCP/.IP 監聽埠和服務匯出的名稱即可。

RmiServiceExporter 主要功能是將服務端遠端物件提供的服務匯出供客戶端請求呼叫,同時將匯出的遠端物件和註冊器繫結起來供客戶端查詢,其主要原始碼如下:

[java] view plaincopyprint?
  1. public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {  
  2.     //匯出的RMI服務名稱   
  3.     private String serviceName;  
  4.     //RMI服務埠   
  5.     private int servicePort = 0;    
  6.     //RMI客戶端socket工廠   
  7.     private RMIClientSocketFactory clientSocketFactory;  
  8.     //RMI服務端socket工廠   
  9.     private RMIServerSocketFactory serverSocketFactory;  
  10.     //註冊器   
  11.     private Registry registry;  
  12.     //註冊主機   
  13.     private String registryHost;  
  14.     //註冊埠   
  15.     private int registryPort = Registry.REGISTRY_PORT;  
  16.     //客戶端註冊socket工廠   
  17.     private RMIClientSocketFactory registryClientSocketFactory;  
  18.     //服務端註冊socket工廠   
  19.     private RMIServerSocketFactory registryServerSocketFactory;  
  20.     //總是建立時註冊   
  21.     private boolean alwaysCreateRegistry = false;  
  22.     //替換已有的繫結   
  23.     private boolean replaceExistingBinding = true;  
  24.     //匯出的遠端物件   
  25.     private Remote exportedObject;  
  26.     //建立註冊   
  27.     private boolean createdRegistry = false;  
  28.     //注入服務端配置的RMI匯出服務名稱,格式為:“rmi://host:post/NAME”   
  29.     public void setServiceName(String serviceName) {  
  30.         this.serviceName = serviceName;  
  31.     }  
  32.     //設定服務埠   
  33.     public void setServicePort(int servicePort) {  
  34.         this.servicePort = servicePort;  
  35.     }  
  36.     //設定RMI客戶端socket工廠   
  37.     public void setClientSocketFactory(RMIClientSocketFactory clientSocketFactory) {  
  38.         this.clientSocketFactory = clientSocketFactory;  
  39.     }  
  40.     //設定RMI服務端socket工廠   
  41.     public void setServerSocketFactory(RMIServerSocketFactory serverSocketFactory) {  
  42.         this.serverSocketFactory = serverSocketFactory;  
  43.     }  
  44.     //設定RMI註冊器   
  45.     public void setRegistry(Registry registry) {  
  46.         this.registry = registry;  
  47.     }  
  48.     //設定RMI註冊主機   
  49.     public void setRegistryHost(String registryHost) {  
  50.         this.registryHost = registryHost;  
  51.     }  
  52.     //設定RMI註冊埠   
  53.     public void setRegistryPort(int registryPort) {  
  54.         this.registryPort = registryPort;  
  55.     }  
  56.     //設定用於註冊的RMI客戶端socket工廠   
  57.     public void setRegistryClientSocketFactory(RMIClientSocketFactory registryClientSocketFactory) {  
  58.         this.registryClientSocketFactory = registryClientSocketFactory;  
  59.     }  
  60. //設定用於註冊的RMI服務端socket工廠   
  61.     public void setRegistryServerSocketFactory(RMIServerSocketFactory registryServerSocketFactory) {  
  62.         this.registryServerSocketFactory = registryServerSocketFactory;  
  63.     }  
  64.     //設定是否總是建立註冊,而不是試圖查詢指定埠上已有的註冊   
  65.     public void setAlwaysCreateRegistry(boolean alwaysCreateRegistry) {  
  66.         this.alwaysCreateRegistry = alwaysCreateRegistry;  
  67.     }  
  68.     //設定是否提供已繫結的RMI註冊   
  69.     public void setReplaceExistingBinding(boolean replaceExistingBinding) {  
  70.         this.replaceExistingBinding = replaceExistingBinding;  
  71.     }  
  72.     //IoC容器依賴注入完成之後的回撥方法   
  73.     public void afterPropertiesSet() throws RemoteException {  
  74.         prepare();  
  75.     }  
  76.     //初始化RMI服務匯出器   
  77.     public void prepare() throws RemoteException {  
  78.         //呼叫其父類RmiBasedExporter的方法,檢查服務引用是否被設定   
  79.         checkService();  
  80.         //如果服務匯出名稱為null   
  81.         if (this.serviceName == null) {  
  82.             throw new IllegalArgumentException("Property 'serviceName' is required");  
  83.         }  
  84.         //檢查socket工廠   
  85.         if (this.clientSocketFactory instanceof RMIServerSocketFactory) {  
  86.             this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory;  
  87.         }  
  88.         if ((this.clientSocketFactory != null && this.serverSocketFactory == null) ||  
  89.                 (this.clientSocketFactory == null && this.serverSocketFactory != null)) {  
  90.             throw new IllegalArgumentException(  
  91.                     "Both RMIClientSocketFactory and RMIServerSocketFactory or none required");  
  92.         }  
  93.         //檢查RMI註冊的socket工廠   
  94.         if (this.registryClientSocketFactory instanceof RMIServerSocketFactory) {  
  95.             this.registryServerSocketFactory = (RMIServerSocketFactory) this.registryClientSocketFactory;  
  96.         }  
  97.         if (this.registryClientSocketFactory == null && this.registryServerSocketFactory != null) {  
  98.             throw new IllegalArgumentException(  
  99.                     "RMIServerSocketFactory without RMIClientSocketFactory for registry not supported");  
  100.         }  
  101.         this.createdRegistry = false;  
  102.         //獲取RMI註冊器   
  103.         if (this.registry == null) {  
  104.             this.registry = getRegistry(this.registryHost, this.registryPort,  
  105.                 this.registryClientSocketFactory, this.registryServerSocketFactory);  
  106.             this.createdRegistry = true;  
  107.         }  
  108.         //獲取要被匯出的服務端遠端物件   
  109.         this.exportedObject = getObjectToExport();  
  110.         if (logger.isInfoEnabled()) {  
  111.             logger.info("Binding service '" + this.serviceName + "' to RMI registry: " + this.registry);  
  112.         }  
  113.         //匯出遠端服務物件   
  114.         if (this.clientSocketFactory != null) {  
  115.             UnicastRemoteObject.exportObject(  
  116.                     this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);  
  117.         }  
  118.         else {  
  119.             UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);  
  120.         }  
  121.         //將RMI物件繫結到註冊器   
  122.         try {  
  123.             if (this.replaceExistingBinding) {  
  124.                 this.registry.rebind(this.serviceName, this.exportedObject);  
  125.             }  
  126.             else {  
  127.                 this.registry.bind(this.serviceName, this.exportedObject);  
  128.             }  
  129.         }  
  130.         //異常處理   
  131.         catch (AlreadyBoundException ex) {  
  132.             unexportObjectSilently();  
  133.             throw new IllegalStateException(  
  134.                     "Already an RMI object bound for name '"  + this.serviceName + "': " + ex.toString());  
  135.         }  
  136.         catch (RemoteException ex) {  
  137.             unexportObjectSilently();  
  138.             throw ex;  
  139.         }  
  140.     }  
  141.     ……  
  142. }   

7. RemoteInvocationBasedExporter 處理 RMI 遠端呼叫請求:

RmiServiceExporter 的父類 RmiBasedExporter 的父類 RemoteInvocationBasedExporter 負責對 RMI 遠端呼叫請求進行處理,並將處理的結果封裝返回,其原始碼如下:

[java] view plaincopyprint?
  1. public abstract class RemoteInvocationBasedExporter extends RemoteExporter {  
  2. //RMI遠端呼叫處理器,RMI遠端呼叫請求是由DefaultRemoteInvocationExecutor處理   
  3.     private RemoteInvocationExecutor remoteInvocationExecutor = new DefaultRemoteInvocationExecutor();  
  4. //設定RMI遠端呼叫處理器   
  5.     public void setRemoteInvocationExecutor(RemoteInvocationExecutor remoteInvocationExecutor) {  
  6.         this.remoteInvocationExecutor = remoteInvocationExecutor;  
  7.     }  
  8.     //獲取RMI遠端呼叫處理器   
  9.     public RemoteInvocationExecutor getRemoteInvocationExecutor() {  
  10.         return this.remoteInvocationExecutor;  
  11.     }  
  12. //對RMI遠端呼叫請求進行處理的地方   
  13.     protected Object invoke(RemoteInvocation invocation, Object targetObject)  
  14.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {  
  15.         if (logger.isTraceEnabled()) {  
  16.             logger.trace("Executing " + invocation);  
  17.         }  
  18.         try {  
  19.             //呼叫DefaultRemoteInvocationExecutor對RMI遠端呼叫請求進行處理   
  20.             return getRemoteInvocationExecutor().invoke(invocation, targetObject);  
  21.         }  
  22.         catch (NoSuchMethodException ex) {  
  23.             if (logger.isDebugEnabled()) {  
  24.                 logger.warn("Could not find target method for " + invocation, ex);  
  25.             }  
  26.             throw ex;  
  27.         }  
  28.         catch (IllegalAccessException ex) {  
  29.             if (logger.isDebugEnabled()) {  
  30.                 logger.warn("Could not access target method for " + invocation, ex);  
  31.             }  
  32.             throw ex;  
  33.         }  
  34.         catch (InvocationTargetException ex) {  
  35.             if (logger.isDebugEnabled()) {  
  36.                 logger.debug("Target method failed for " + invocation, ex.getTargetException());  
  37.             }  
  38.             throw ex;  
  39.         }  
  40.     }  
  41.     //獲取RMI遠端呼叫請求的處理結果   
  42.     protected RemoteInv