我有一个界面,例如:
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/