`
whitesock
  • 浏览: 479178 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Spring RMI

阅读更多

    Spring RMI 不需要输出的接口继承自Remote(这也是Spring一直坚持的原则之一: 业务逻辑不应该同远程逻辑一起设计)。但是这是如何做的呢, 答案是代理。以下是一个简化版的例子程序,用于模拟Spring的实现。

    首先是用于发布的接口及实现:
    MockProduct.java

public interface MockProduct {
    String getDescription();
}


    MockProductRemote.java

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface MockProductRemote extends Remote {
    String getDescription() throws RemoteException;
}


    MockProductImpl.java

public class MockProductImpl implements MockProduct {
    public String getDescription() {
        return "Mock Product";
    }
}


    MockProductRemoteImpl.java

import java.rmi.RemoteException;

public class MockProductRemoteImpl implements MockProductRemote {
    public String getDescription() throws RemoteException {
        return "Mock Product Remote";
    }
}


    接下来是用于输出RMI服务的类MockRmiServiceExporter.java

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import org.springframework.aop.framework.ProxyFactory;

public class MockRmiServiceExporter {
    //
    private Object service;
    private String serviceName;
    private Class<?> serviceInterface;
    
    //
    private Remote exportedObject;
    
    public Object invoke(MockRemoteInvocation invocation, Object wrappedObject) throws RemoteException {
        return invocation.invoke(wrappedObject);
    }

    public void afterPropertiesSet() throws Throwable {
        Registry reg = getRegistry();
        this.exportedObject = getObjectToExport();
        UnicastRemoteObject.exportObject(this.exportedObject);
        reg.rebind(this.serviceName, this.exportedObject);
    }
    
    public Object getService() {
        return service;
    }

    public void setService(Object service) {
        this.service = service;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public Class<?> getServiceInterface() {
        return serviceInterface;
    }

    public void setServiceInterface(Class<?> serviceInterface) {
        this.serviceInterface = serviceInterface;
    }
    
    private Registry getRegistry() throws RemoteException {
        try {
            Registry reg = LocateRegistry.getRegistry(Registry.REGISTRY_PORT);
            reg.list(); // Test it
            return reg;
        } catch (RemoteException ex) {
            return LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        }
    }
    
    private Remote getObjectToExport() {
        // determine remote object
        if (getService() instanceof Remote &&
                ((getServiceInterface() == null) || Remote.class.isAssignableFrom(getServiceInterface()))) {
            // conventional RMI service
            return (Remote) getService();
        } else {
            // RMI invoker
            return new MockRmiInvocationWrapper(getProxyForService(), this);
        }
    }
    
    protected Object getProxyForService() {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addInterface(getServiceInterface());
        proxyFactory.setTarget(getService());
        return proxyFactory.getProxy();
    }
}

    在这个段代码中是用LocateRegistry类的相关方法代替了启动rmiregistry。 在getObjectToExport方法中,如果getService()和getServiceInterface()都没有实现Remote接口,那么实际发布的是MockRmiInvocationWrapper; 相反,如果getService()和getServiceInterface()都实现了Remote接口, 那么实际发布的就是这个Remote接口本身, 这意味着客户端可以不用使用Spring的RMI客户端实现,而是使用传统RMI的方式。

    为了保证在不同版本的jdk下都能正确执行,需要手动用rmic为MockRmiInvocationWrapper生成一个stub(但是对于待发布的接口来说,便不再需要stub)。如果使用UnicastRemoteObject.exportObject(this.exportedObject, 0)方法来发布对象,那么连这个stub也可以不必生成。顺便说一下,在Spring的二进制发布包spring-context.jar中有一个RmiInvocationWrapper_Stub.class便是完成类似的功能。

 

    再接下来是MockRmiInvocationWrapper.java的代码, 它实现了Remote接口。

import java.rmi.RemoteException;

public class MockRmiInvocationWrapper implements MockRmiInvocationHandler {
    private Object wrappedObject;
    private MockRmiServiceExporter rmiExporter;
    
    public MockRmiInvocationWrapper(Object wrappedObject, MockRmiServiceExporter rmiExporter) {
        this.wrappedObject = wrappedObject;
        this.rmiExporter = rmiExporter;
    }

    public Object invoke(MockRemoteInvocation invocation) throws RemoteException {
        return this.rmiExporter.invoke(invocation, this.wrappedObject);
    }
}


    MockRmiInvocationHandler.java

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface MockRmiInvocationHandler extends Remote {
    Object invoke(MockRemoteInvocation invocation) throws RemoteException;
}


    MockRemoteInvocation.java

import java.io.Serializable;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import org.aopalliance.intercept.MethodInvocation;

public class MockRemoteInvocation implements Serializable{
    //
    private static final long serialVersionUID = 1044994565810431680L;
    
    //
    private String methodName;
    private Class<?>[] parameterTypes;
    private Object[] arguments;
    
    public MockRemoteInvocation(MethodInvocation methodInvocation) {
        this.methodName = methodInvocation.getMethod().getName();
        this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
        this.arguments = methodInvocation.getArguments();
    }
    
    public Object invoke(Object targetObject) throws RemoteException {
        try {
            Method method = targetObject.getClass().getMethod(this.methodName, this.parameterTypes);
            return method.invoke(targetObject, this.arguments);
        } catch (Exception e) {
            throw new RemoteException(e.toString());
        }
    }
    
    public String getMethodName() {
        return methodName;
    }
    
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    
    public Class<?>[] getParameterTypes() {
        return parameterTypes;
    }
    
    public void setParameterTypes(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }
    
    public Object[] getArguments() {
        return arguments;
    }
    
    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }
}

 
    再接下来是客户端的相关类。在MockRmiProxyFactoryBean中,它使用MethodInterceptor拦截了所有对于导出接口方法的调用并转发给相应的stub。如果这个stub是实现了MockRmiInvocationHandler(比如MockRmiInvocationWrapper), 那么会把所有的调用转发给MockRmiInvocationHandler的invoke方法,同时构造一个MockRemoteInvocation。此后就是通过MockRmiInvocationWrapper_Stub进行远程调用了。

    服务器端接收到请求之后,在MockRmiInvocationWrapper类中又把这个请求转发到this.rmiExporter.invoke(invocation, this.wrappedObject)。在MockRmiServiceExporter的invoke方法中,再次转发给MockRemoteInvocation的invoke方法, 最终在MockRemoteInvocation.inovke方法中调用了targetObject上的相关方法。顺便说一下,由于MockRemoteInvocation对象是从客户端通过序列化传递到服务器端的, 而且在这个对象中保存了客户端调用的方法名及参数列表,所以在这个对象中能最终决定应该调用targetObject上的哪个方法。
    MockRmiProxyFactoryBean.java

import java.lang.reflect.Method;
import java.rmi.Naming;
import java.rmi.Remote;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;

public class MockRmiProxyFactoryBean implements FactoryBean, MethodInterceptor {
    //
    private Object serviceProxy;
    
    //
    private String serviceUrl;
    private Class<?> serviceInterface;
    
    public Object getObject() {
        return serviceProxy;
    }
    
    @SuppressWarnings("unchecked")
    public Class getObjectType() {
        return getServiceInterface();
    }

    public boolean isSingleton() {
        return true;
    }
    
    protected Remote getStub() throws Throwable {
        Remote stub = Naming.lookup(getServiceUrl());
        return stub;
    }
    
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Remote stub = null;
        try {
            stub = getStub();
        } catch (Throwable ex) {
            throw ex;
        }
        try {
            return doInvoke(invocation, stub);
        } catch (Exception ex) {
            throw ex;
        }
    }
    
    protected Object doInvoke(MethodInvocation mi, Remote stub) throws Throwable {
        if (stub instanceof MockRmiInvocationHandler) {
            // RMI invoker
            try {
                return doInvoke(mi, (MockRmiInvocationHandler)stub);
            } catch (Exception ex) {
                throw ex;
            }
        } else {
            // traditional RMI stub
            try {
                Method method = mi.getMethod();
                if (method.getDeclaringClass().isInstance(stub)) {
                    // directly implemented
                    return method.invoke(stub, mi.getArguments());
                } else {
                    // not directly implemented
                    Method stubMethod = stub.getClass().getMethod(method.getName(), method.getParameterTypes());
                    return stubMethod.invoke(stub, mi.getArguments());
                }
            } catch (Exception ex) {
                throw ex;
            }
        }
    }
    
    protected Object doInvoke(MethodInvocation mi, MockRmiInvocationHandler mrih) throws Throwable {
        return mrih.invoke(createRemoteInvocation(mi));
    }
    
    protected MockRemoteInvocation createRemoteInvocation(MethodInvocation mi) throws Throwable  {
        return new MockRemoteInvocation(mi);
    }

    public void afterPropertiesSet() {
        serviceProxy = ProxyFactory.getProxy(getServiceInterface(), this);
    }
    
    public String getServiceUrl() {
        return serviceUrl;
    }
    
    public void setServiceUrl(String serviceUrl) {
        this.serviceUrl = serviceUrl;
    }
 
    public Class<?> getServiceInterface() {
        return serviceInterface;
    }
    
    public void setServiceInterface(Class<?> serviceInterface) {
        this.serviceInterface = serviceInterface;
    }
}

    以上的例子可以看成Spring实现的一个简化版本,其中去掉了复杂的异常处理,一些类的继承关系等。但是保留了基本的控制逻辑,有助于加深理解。

 

    最后是编写两个测试类,检验一下工作成果了。执行前需要生成MockRmiInvocationWrapper的存根类。另外由于MockProductRemote继承了Remote接口,因此不是以MockRmiInvocationWrapper发布的,所以还要生成MockProductRemoteImpl的存根类。
    MockRmiServer.java

public class MockRmiServer {
    public static void main(String args[]) {
        try {
            //
            System.out.println("starting mock server...");
            MockProduct mp = new MockProductImpl();
            MockRmiServiceExporter rmiExporter1 = new MockRmiServiceExporter();
            rmiExporter1.setService(mp);
            rmiExporter1.setServiceInterface(MockProduct.class);
            rmiExporter1.setServiceName("mockProduct");
            rmiExporter1.afterPropertiesSet();
            
            // rmic MockProductRemoteImpl
            MockProductRemote mpr = new MockProductRemoteImpl();
            MockRmiServiceExporter rmiExporter2 = new MockRmiServiceExporter();
            rmiExporter2.setService(mpr);
            rmiExporter2.setServiceInterface(MockProductRemote.class);
            rmiExporter2.setServiceName("mockProductRemote");
            rmiExporter2.afterPropertiesSet();
            System.out.println("done");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}


    MockRmiClient.java

public class MockRmiClient {
    public static void main(String args[]) {
        try {
            //
            System.out.println("starting mock client...");
            MockRmiProxyFactoryBean factoryBean1 = new MockRmiProxyFactoryBean();
            factoryBean1.setServiceUrl("rmi://127.0.0.1:1099/mockProduct");
            factoryBean1.setServiceInterface(MockProduct.class);
            factoryBean1.afterPropertiesSet();
            MockProduct mp = (MockProduct)factoryBean1.getObject();
            System.out.println("got mock product: " + mp.getDescription());
            
            //
            MockRmiProxyFactoryBean factoryBean2 = new MockRmiProxyFactoryBean();
            factoryBean2.setServiceUrl("rmi://127.0.0.1:1099/mockProductRemote");
            factoryBean2.setServiceInterface(MockProductRemote.class);
            factoryBean2.afterPropertiesSet();
            MockProductRemote mpr = (MockProductRemote)factoryBean2.getObject();
            System.out.println("got mock product remote: " + mpr.getDescription());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}
8
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics