SpringRMI解析3-RmiServiceExporter邏輯細節


在發布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為之前封裝的代理類。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM