一个基于RMI的小巧好用的框架
程序员文章站
2022-07-11 17:55:46
...
最近有用到rmi技术,需要实现Remote接口,需要抛出RemoteException异常,极其不爽,遂封装之,封装之后只需普通
POJO对象即可!
该框架核心有三个类,一个服务端类RMIEndpoint,一个客户端类RMIProxyFactory,一个通用接口GenericRmiService
先看一下用法:
服务端用法:
RMIEndpoint.addService(User.class, new UserImpl());
try {
RMIEndpoint.publish(8888);
} catch (Exception e) {
e.printStackTrace();
}
客户端用法:
try {
User user = RMIProxyFactory.getProxy(User.class, "localhost", 8888);
System.out.println(user.say("11111"));
System.out.println(user.say("11111"));
System.out.println(user.say("11111"));
} catch (Exception e) {
e.printStackTrace();
}
用起来简单吧!User是普通的接口,UserImpl是普通的POJO对象.
下面来看看三个核心类内部。
服务端点类:
package com.yuan.common.rmi;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yuan.common.util.ReflectUtil;
public class RMIEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(RMIEndpoint.class);
private static ConcurrentMap<String, Object> serviceMap = new ConcurrentHashMap<String, Object>();
private static String HOSTNAME;
public static void addService(Object service){
serviceMap.put(service.getClass().getName(), service);
}
public static void addService(Class<?> iface, Object service){
serviceMap.put(iface.getName(), service);
}
/**
* 服务端多个IP则必须设置
* @param hostName
*/
public static void setHostName(String hostName){
System.setProperty("java.rmi.server.hostname" , hostName);
HOSTNAME = hostName;
}
public static void publish() throws RemoteException, MalformedURLException, UnknownHostException{
publish(1099);
}
public static void publish(int port) throws RemoteException, MalformedURLException, UnknownHostException{
LocateRegistry.createRegistry(port); //注册端口
GenericRmiService rmiService = new GenericRmiService(){
private static final long serialVersionUID = 1L;
public Object doService(String serviceName, String methodName, Object[] args) throws RemoteException {
if(serviceMap.containsKey(serviceName)){
Object service = serviceMap.get(serviceName);
try {
return ReflectUtil.execMethod(service, methodName, args);
} catch (Exception e) {
LOG.warn(e.getMessage(), e);
throw new RemoteException(e.getMessage(), e);
}
}
LOG.warn("RMI服务" + serviceName + "不存在!");
throw new RemoteException("RMI服务" + serviceName + "不存在!");
}
};
UnicastRemoteObject.exportObject(rmiService, 0); //随机通信端口
if(HOSTNAME == null){
HOSTNAME = InetAddress.getLocalHost().getHostAddress();
}
Naming.rebind("rmi://"+HOSTNAME+":"+port+"/GenericRmiService", rmiService);
}
}
客户端代理工厂类:
package com.yuan.common.rmi;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.Naming;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class RMIProxyFactory {
private static final ConcurrentMap<String, Object> proxyCache = new ConcurrentHashMap<String, Object>();
public static void clearProxyCache(){
proxyCache.clear();
}
public static <T> T getProxy(Class<T> iface, String host, int port) throws Exception{
final String serviceName = getServiceName(iface);
if(proxyCache.containsKey(serviceName)){
return (T)proxyCache.get(serviceName);
}
final GenericRmiService rmiService = (GenericRmiService)Naming.lookup("rmi://"+host+":"+port+"/GenericRmiService");
InvocationHandler h = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
try {
Object r = rmiService.doService(serviceName, method.getName(), args);
return r;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
};
Object proxy = Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[]{iface}, h);
proxyCache.put(serviceName, proxy);
return (T) proxy;
}
private static <T> String getServiceName(Class<T> iface) throws IllegalArgumentException, IllegalAccessException{
Field[] fs = iface.getFields();
for(Field f : fs){
if(f.getName().equals("SERVICENAME")){
return (String)f.get(null);
}
}
return iface.getName();
}
}
RMI通用接口:
package com.yuan.common.rmi;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface GenericRmiService extends Remote, Serializable {
public Object doService(String serviceName, String methodName, Object[] args)throws RemoteException;
}
User接口:
package tmp.rmi;
public interface User {
public String say(String msg);
}
UserImpl对象:
package tmp.rmi;
public class UserImpl implements User {
@Override
public String say(String msg) {
System.out.println("=== " + msg);
System.out.println("=== " + Thread.currentThread());
return "qqqqqqqqq";
}
}
注意RMI支持并发访问,所以UserImpl必须是线程安全的!
附件中是完整源代码!
推荐阅读
-
基于vue框架手写一个notify插件实现通知功能的方法
-
JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC
-
基于vue实现一个简单的MVVM框架
-
一个基于 .NET Core 2.0 开发的简单易用的快速开发框架 - LinFx
-
一个轻量级的基于 .NET Core 的 ORM 框架 HSQL
-
分享一个基于jQuery,backbone.js和underscore.js的消息提示框架 - Backbone.Notifier
-
Kute一个非常好用的Javascript动画框架 javascript
-
一个基于POM模式的Web自动化测试框架
-
一个高可扩展的基于非阻塞IO的服务器架构 框架javasocket工作编程
-
一个基于RMI的小巧好用的框架