zoukankan      html  css  js  c++  java
  • 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为之前封装的代理类。

  • 相关阅读:
    枚举和字符串转换
    JavaScript string.format
    Jquery 中toggle的用法举例
    JQuery 实现倒计时
    找位置
    图算法
    文件操作总结
    动态规划——总结
    回文字符串
    a+b_1
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/6086620.html
Copyright © 2011-2022 走看看