在發布RMI服務的流程中,有幾個步驟可能是我們比較關心的。
獲取registry
由於底層的封裝,獲取Registry實例是非常簡單的,只需要使用一個函數LocateRegistry.createRegistry(...)創建Registry實例就可以了。但是,Spring中並沒有這么做,而是考慮得更多,比如RMI注冊主機與發布的服務並不在一台機器上,那么需要使用LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory)去遠程獲取Registry實例。
protected Registry getRegistry(String registryHost, int registryPort, RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory) throws RemoteException { if (registryHost != null) { // Host explicitly specified: only lookup possible. if (logger.isInfoEnabled()) { logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]"); } //如果registryHost不為空則嘗試獲取對應主機的Registry Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory); //遠程連接測試 testRegistry(reg); return reg; } else { //獲取本機的Registry return getRegistry(registryPort, clientSocketFactory, serverSocketFactory); } }
如果並不是從另外的服務器上獲取Registry連接,那么就需要在本地創建RMI的Registry實例了。當然,這里有一個關鍵的參數alwaysCreateRegistry,如果此參數配置為true,那么在獲取Registry實例時會首先測試是否已經建立了對指定端口的連接,如果已經建立則復用已經創建的實例,否則重新創建。
當然,之前也提到過,創建Registry實例時可以使用自定義的連接工廠,而之前的判斷也保證了clientSocketFactory與serverSocketFactory要么同時出現,要么同時不出現,所以這里只對clientSocketFactory是否為空進行了判斷。
protected Registry getRegistry( int registryPort, RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory) throws RemoteException { if (clientSocketFactory != null) { if (this.alwaysCreateRegistry) { logger.info("Creating new RMI registry"); //使用clientSocketFactory創建Registry return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory); } if (logger.isInfoEnabled()) { logger.info("Looking for RMI registry at port '" + registryPort + "', using custom socket factory"); } synchronized (LocateRegistry.class) { try { // Retrieve existing registry. //復用測試 Registry reg = LocateRegistry.getRegistry(null, registryPort, clientSocketFactory); testRegistry(reg); return reg; } catch (RemoteException ex) { logger.debug("RMI registry access threw exception", ex); logger.info("Could not detect RMI registry - creating new one"); // Assume no registry found -> create new one. return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory); } } } else { return getRegistry(registryPort); } } //如果創建Registry實例時不需要使用自定義的套接字工廠,那么就可以直接使用LocateRegistry.createRegistry(...)方法來創建了,當然復用的檢測還是必要的。 protected Registry getRegistry(int registryPort) throws RemoteException { if (this.alwaysCreateRegistry) { logger.info("Creating new RMI registry"); return LocateRegistry.createRegistry(registryPort); } if (logger.isInfoEnabled()) { logger.info("Looking for RMI registry at port '" + registryPort + "'"); } synchronized (LocateRegistry.class) { try { // Retrieve existing registry. //查看對應當前registryPort的Registry是否已經創建,如果創建直接使用 Registry reg = LocateRegistry.getRegistry(registryPort); //測試是否可用,如果不可用則拋出異常 testRegistry(reg); return reg; } catch (RemoteException ex) { logger.debug("RMI registry access threw exception", ex); logger.info("Could not detect RMI registry - creating new one"); // Assume no registry found -> create new one. //根據端口創建Registry return LocateRegistry.createRegistry(registryPort); } } }
初始化將要導出的實體對象
當請求某個RMI服務的時候,RMI會根據注冊的服務名稱,將請求引導至遠程對象處理類中,這個處理類便是使用getObjectToExport()進行創建。
protected Remote getObjectToExport() { // determine remote object //如果配置的service屬性對應的類實現了Remote接口且沒有配置serviceInterface屬性 if (getService() instanceof Remote && (getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) { // conventional RMI service return (Remote) getService(); } else { // RMI invoker if (logger.isDebugEnabled()) { logger.debug("RMI service [" + getService() + "] is an RMI invoker"); } //對service進行封裝 return new RmiInvocationWrapper(getProxyForService(), this); } }
請求處理類的初始化主要處理規則為:如果配置的service屬性對應的類實現了Remote接口且沒有配置serviceInterface屬性,那么直接使用service作為處理類;否則,使用RMIInvocationWrapper對service的代理類和當前類也就是RMIServiceExporter進行封裝。
經過這樣的封裝,客戶端與服務端便可以達成一致協議,當客戶端檢測到是RMIInvocationWrapper類型stub的時候便會直接調用其invoke方法,使得調用端與服務端很好地連接在了一起。而RMIInvocationWrapper封裝了用於處理請求的代理類,在invoke中便會使用代理類進行進一步處理。當請求RMI服務時會由注冊表Registry實例將請求轉向之前注冊的處理類去處理,也就是之前封裝的RMIInvocationWrapper,然后由RMIInvocationWrapper中的invoke方法進行處理,那么為什么不是在invoke方法中直接使用service,而是通過代理再次將service封裝呢?這其中的一個關鍵點是,在創建代理時添加了一個增強攔截器RemoteInvocationTraceInterceptor,目的是為了對方法調用進行打印跟蹤,但是如果直接在invoke方法中硬編碼這些日志,會使代碼看起來很不優雅,而且耦合度很高,使用代理的方式就會解決這樣的問題,而且會有很高的可擴展性。
protected Object getProxyForService(){ //驗證service checkService(); //驗證serviceInterface checkServiceInterface(); //使用JDK的方式創建代理 ProxyFactory proxyFactory = new ProxyFactory(); //添加代理接口 proxyFactory.addInterface(getServiceInterface()); if(registerTraceInterceptor == null ? interceptors == null : registerTraceInterceptor.booleanValue()) //加入代理的橫切面RemoteInvocationTraceInterceptor並記錄Exporter名稱 proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName())); if(interceptors != null) { AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); for(int i = 0; i < interceptors.length; i++) proxyFactory.addAdvisor(adapterRegistry.wrap(interceptors[i])); } //設置要代理的目標類 proxyFactory.setTarget(getService()); proxyFactory.setOpaque(true); //創建代理 return proxyFactory.getProxy(getBeanClassLoader()); }
RMI服務激活調用
由於在之前bean初始化的時候做了服務名稱綁定this.registry.bind(this.serviceName,thhis.exportedObjedt),其中的exportedObject其實是被RMIInvocationWrapper進行封裝過的,也就是說當其他服務調用serviceName的RMI服務時,Java會為我們封裝其內部操作,而直接會將代碼轉向RMIInvocationWrapper測invoke方法中。
public Object invoke(RemoteInvocation invocation) throws RemoteException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException{ return rmiExporter.invoke(invocation, wrappedObject); }
而此時this.rmiExporter為之前初始化的RMIServiceExporter,invocation為包含着需要激活的方法參數,而wrappedObject則是之前封裝的代理類。
protected Object invoke(RemoteInvocation invocation, Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { return super.invoke(invocation, targetObject); } protected Object invoke(RemoteInvocation invocation, Object targetObject) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException { if(logger.isTraceEnabled()) logger.trace((new StringBuilder()).append("Executing ").append(invocation).toString()); try { return getRemoteInvocationExecutor().invoke(invocation, targetObject); }catch(NoSuchMethodException ex){ if(logger.isDebugEnabled()) logger.warn((new StringBuilder())
.append("Could not find target method for ")
.append(invocation).toString(), ex); throw ex; } catch(IllegalAccessException ex){ if(logger.isDebugEnabled()) logger.warn((new StringBuilder())
.append("Could not access target method for ")
.append(invocation).toString(), ex); throw ex; }catch(InvocationTargetException ex){ if(logger.isDebugEnabled()) logger.debug((new StringBuilder()).append("Target method failed for ")
.append(invocation).toString(), ex.getTargetException()); throw ex; } } public Object invoke(RemoteInvocation invocation, Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{ Assert.notNull(invocation, "RemoteInvocation must not be null"); Assert.notNull(targetObject, "Target object must not be null"); //通過反射方式激活方法 return invocation.invoke(targetObject); } public Object invoke(Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { //根據方法名稱獲取代理中的方法 Method method = targetObject.getClass().getMethod(methodName, parameterTypes); //執行代理中方法 return method.invoke(targetObject, arguments); }
targetObject為之前封裝的代理類。