java - 将接口(interface)转换为RMI接口(interface)

标签 java interface rmi bytecode

我有一个界面,例如:

interface MyService {
  String sayHello(Object who) throws ServiceException;
}

现在我需要使用 RMI 接口(interface)。当然我可以定义另一个接口(interface),例如:

interface MyService extends Remote{
  String sayHello(Object who) throws RemoteException;
}

然后使我的实现类适应 RMI 接口(interface)。

但是我有数百个接口(interface)要转换为 RMI。编写数百个具有相同逻辑的类是如此无聊和丑陋。

那么有没有一种简单的方法可以在 RMI 中使用这些接口(interface)呢?

最佳答案

这是我作品中的真实场景。我使用动态字节码一次性完成了数百项工作。

首先,这是我的旧代码演示(没有 RMI)。

class ServiceException extends Exception {
  public ServiceException(String msg) {
    super(msg);
  }
}

interface MyService {
  String sayHello(Object who) throws ServiceException;

  void throwIt() throws ServiceException;
}

class MyServiceImpl implements MyService {

  @Override
  public String sayHello(Object who) throws ServiceException {
    String hello = who.toString();
    System.out.println("Server said: " + hello);
    return "Hello! " + hello;
  }

  @Override
  public void throwIt() throws ServiceException {
    throw new ServiceException("throw in server");
  }
}

我使用Javassist将接口(interface)转换为RMI接口(interface):

  public static Class<? extends Remote> toRemoteInterface(Class<?> inter) throws Exception {
    return cache("toRemote", inter, () -> uncheck(() -> {
      ClassPool pool = ClassPool.getDefault();
      CtClass cc = pool.getAndRename(inter.getName(), inter.getName() + "$RemoteVersion");
      cc.setModifiers(Modifier.PUBLIC | cc.getModifiers());
      cc.addInterface(pool.get(Remote.class.getName()));
      for (CtMethod cm : cc.getMethods()) {
        cm.setExceptionTypes(new CtClass[] { pool.getCtClass(RemoteException.class.getName()) });
      }
      cc.writeFile();
      return cc.toClass();
    }));
  }

然后,使用Cglib在远程对象和本地对象之间进行转换:

  public static <T> T fromRemote(Remote remote, Class<T> inter) throws Exception {
    Enhancer e = new Enhancer();
    e.setInterfaces(new Class[] { inter });
    e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
      Method remoteMethod = remote.getClass().getMethod(method.getName(), method.getParameterTypes());
      try {
        return remoteMethod.invoke(remote, args);
      } catch (InvocationTargetException ex) {
        Throwable targetException = ex.getTargetException();
        while (targetException instanceof RemoteException) {
          targetException = targetException.getCause();
        }
        throw targetException;
      }
    });
    return (T) e.create();
  }

  public static <T> Remote toRemote(T local, Class<T> inter) throws Exception {
    Enhancer e = new Enhancer();
    e.setSuperclass(UnicastRemoteObject.class);
    e.setInterfaces(new Class[] { toRemoteInterface(inter) });
    e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
      Method targetMethod = local.getClass().getMethod(method.getName(), method.getParameterTypes());
      try {
        return targetMethod.invoke(local, args);
      } catch (InvocationTargetException ex) {
        Throwable targetException = ex.getTargetException();
        throw new RemoteException(targetException.getMessage(), targetException);
      }
    });
    return (Remote) e.create();
  }

现在我们可以像使用 RMI 接口(interface)一样使用非 rmi 接口(interface)及其实现:

  public static void startClient() throws Exception {
    String stringURL = "rmi://127.0.0.1/" + MyService.class.getName();
    toRemoteInterface(MyService.class);// define the Remote interface in client classloader
    MyService service = fromRemote(Naming.lookup(stringURL), MyService.class);
    String said = service.sayHello("Dean");
    System.out.println("Client heard: " + said);
    service.throwIt();
  }

  public static void startServer() throws Exception {
    LocateRegistry.createRegistry(1099);
    Remote remote = toRemote(new MyServiceImpl(), MyService.class);
    Naming.rebind(MyService.class.getName(), remote);
    System.out.println(remote);
    System.out.println(remote.getClass());
    System.out.println("Server started!");
  }

完整代码,see this on github

关于java - 将接口(interface)转换为RMI接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47384900/

相关文章:

java - 如何在 Eclipse 中查找函数返回值未计算的所有实例?

java基准测试: More time for lesser input

java.lang.NoClassDefFoundError : javax/mail/Address error 错误

eclipse - 将 WindowBuilder 集成到现有的 eclipse 项目中

Java接口(interface)和扩展ArrayList

java - 如何从 Android 应用程序调用远程方法

java - RMI over SSL - 通信速度慢

java - 如何使用 AudioTrack 循环音频?

java - 同一主机上的 2 个实例之间的 ehcache 自动发现(通过多播)

c# - 如何在构造函数中设置属性值(显式接口(interface)实现)