公开为RmiServiceExporter代理的服务的引用返回值(或RMI的替代)

时间:2014-03-10 11:38:42

标签: java spring rmi

如果我通过带有RmiServiceExporter的RMI导出服务,则服务调用的返回值将被序列化并通过线路发送到客户端。但除非我有一个只返回数据的简单服务,否则这不是很有用。

我希望能够在此返回值上调用方法,并让它们在服务器上运行,而不是在客户端上运行。基本上,我想要在客户端上创建返回值的代理。有没有办法用RmiServiceExporter(或Spring支持的另一个远程处理选项)执行此操作?


我尝试实现一些返回值的自动换行,这就是我想出来的:

在服务器端:

class RmiReturnValueStubbingExporter extends RmiServiceExporter {

    /* we can't use the Spring implementation of RmiInvocationHandler
     * because it's package protected */
    private class RmiReturnValueInvocationWrapper implements RmiInvocationHandler {
        private final Object wrappedObject;

        private RmiReturnValueInvocationWrapper(Class interf, Object wrappedObject) {
            this.wrappedObject = createProxyFor(interf, wrappedObject);
        }

        @Override
        public String getTargetInterfaceName() throws RemoteException {
            return null;
        }

        @Override
        public Object invoke(RemoteInvocation invocation) throws
                RemoteException, NoSuchMethodException, IllegalAccessException,
                InvocationTargetException {
            return RmiReturnValueStubbingExporter.this.invoke(
                    invocation, this.wrappedObject);
        }
    }

    @Override
    protected Object invoke(RemoteInvocation invocation, Object targetObject)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Object ret = maybeWrap(super.invoke(invocation, targetObject));

        if (ret instanceof Remote) {
            try {
                UnicastRemoteObject.exportObject((Remote) ret, 0);
            } catch (RemoteException e) {
                throw new InvocationTargetException(e);
            }
        }

        return ret;
    }

    private Object maybeWrap(Object superValue) {
        if (superValue instanceof OntologyTerm) {
            return new RmiReturnValueInvocationWrapper(
                    OntologyTerm.class, superValue);
        } else if (superValue instanceof List) {
            return new RmiReturnValueInvocationWrapper(
                    List.class, superValue);
        } else {
            return superValue;
        }
    }

    private Object createProxyFor(Class interf, Object object) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addInterface(interf);
        proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));

        proxyFactory.setTarget(object);
        // don't make opaque, on the client we need to know the interfaces implemented
        return proxyFactory.getProxy(getBeanClassLoader());
    }
}

并在客户端:

class RmiStubReturnValueProxyFactoryBean extends RmiProxyFactoryBean {

    @Override
    protected Object doInvoke(MethodInvocation methodInvocation,
                              RmiInvocationHandler invocationHandler) {

        def superValue = super.doInvoke(methodInvocation, invocationHandler)

        if (superValue instanceof java.lang.reflect.Proxy &&
                java.lang.reflect.Proxy.getInvocationHandler(superValue)
                instanceof RemoteObjectInvocationHandler) {
            RmiInvocationHandler rih = superValue

            def proxiedInterfaces = rih.invoke(
                    new RemoteInvocation('getProxiedInterfaces',
                            [] as Class[], [] as Object[]))

            def clientProxy = new ProxyFactory(proxiedInterfaces)

            clientProxy.addAdvice({ MethodInvocation invoc ->
                doInvoke(invoc, rih)
            } as MethodInterceptor)

            clientProxy.getProxy beanClassLoader
        } else {
            superValue
        }
    }
}

这似乎可以很好地处理两个层次;在这种情况下,服务返回List<OntologyTerm>。我怀疑这有一些问题,首先是调用UnicastRemoteObject.exportObject而没有回收任何东西。这里可能还缺少的另一件事是处理客户端作为服务参数获取的这些存根的传递。因为参数是在客户端上创建的Proxy个对象(包装RMI存根),我想至少服务器必须从客户端加载类,这是我想要避免的。< / p>

我的问题是,是否已经实现了这样的东西(不一定是RMI),我可以很容易地指定哪些类型作为值返回并序列化,以及哪些类型的返回值应该被存根。

3 个答案:

答案 0 :(得分:1)

这是一个古老的问题,它产生了CORBA,Jini和EJB的第一个版本等技术。这个想法大多放弃了,因为它根本不能很好地扩展。

由于网络开销,无法将远程对象透明地视为本地对象。由于网络延迟,在调用远程对象时,您希望在远程调用中打包最多的数据, 而在本地对象中,您希望保持方法调用的最细粒度,以便从OO的角度来看它们是最可重用的。

方法可重用性和低网络开销冲突的这两个要求以及最终结果是远程对象的API总是与本地对象的API非常不同, 即使技术存在使远程对象的调用完全透明。

现在广泛采用的解决方案是通过线路发送DTOs (Data Transfer Objects),这是仅数据对象。这可以在RMI,HTTP(REST),SOAP或其他协议中完成。

然后在每一端都可以对域模型进行一些映射,以便可以在其中写入服务和持久层。另一方面,DTO模型并不意味着跨越表示层。

答案 1 :(得分:1)

  

我的问题是,是否已经实施了这样的事情(不是   必须与RMI),我可以很容易地指定什么类型   作为值返回并序列化以及返回值的类型   应该是残缺的。

首先我想建议的是你应该摆脱RMI。当你想到它时,RMI的水平相当低。 为什么要一直回到CORBA? 只有回答这个问题可能是您不需要Java EE EJB应用服务器。但是你必须保持客户端和服务器JVM同步。如果不升级服务器,则不升级客户端。您必须编写EJB应用服务器为您提供的所有服务(例如,连接池,命名和目录服务,池,请求排队,事务等)。

EJB与RMI

EJB构建于RMI之上。两者都暗示Java客户端和bean。如果您的客户需要用其他东西编写(例如,.NET,PHP等),请使用Web服务或其他与平台无关的有线协议,如HTTP或XML over HTTP或SOAP。

Spring vs EJB

EJB 3.0与Spring相比,更好的选择取决于您是否喜欢POJO开发,除了其他方面还需要选择除ORM和JPA之外的关系技术。 EJB 3.0的优点是EJB 3.0是许多供应商的规范; Spring只能来自Spring Source。春天非常坚固,有很多牵引力,不会去任何地方。它会打开您的所有选项。

<强> Web服务

Spring的Web服务模块非常灵活。在POJO服务接口方面很棒。这些将允许您获得所需的概念隔离,将部署选择推迟到最后一刻,并且如果第一个想法不能很好地让您改变主意。 Web服务理论上很棒,但是有一些问题需要注意:

  • 延迟。福勒的第一个分布式对象定律:“不要!”由许多细粒度分布式SOAP服务组成的体系结构将像糖蜜一样优雅,美观和缓慢。在分发之前要仔细考虑。
  • 从XML到对象和后台的编组消耗了除了允许客户端使用与平台无关的协议之外没有提供任何业务价值的CPU周期。
  • SOAP是一种日常变得越来越臃肿和复杂的标准,但它有很多工具支持。供应商喜欢它,因为它有助于推动ESB的销售。 REST很简单但不太清楚。它不受工具支持。

最后,我建议使用Spring Web Services

答案 2 :(得分:0)

如果任何远程方法的返回值是导出的远程对象,则将其编组为客户端作为其存根。