最近有用到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 ConcurrentMapserviceMap = new ConcurrentHashMap (); 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 ConcurrentMapproxyCache = new ConcurrentHashMap (); public static void clearProxyCache(){ proxyCache.clear(); } public static T getProxy(Class 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 String getServiceName(Class 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必须是线程安全的!
附件中是完整源代码!