RMI(Remote Method Invocation)远程方法调用
客户端向服务器端发送一个“请求“,服务器端处理该请求并将结果返回给客户端。如果把请求看成是一个”本地方法“,在客户端执行该方法,而实质上,在服务器端存在该方法的”本体“。
如上图所示,客户端通过代理,获取”代理对象“,执行相关方法,实质上是向服务器发送请求,请求客户端原本要执行的方法。服务器端维持一个”方法池“,将”远程对象“存储在池子中,起着RMI中注册表的作用。客户端连接服务器,网络发送请求需要将方法名称,参数类型,参数的值等信息发送过去。服务器端必须能够建立服务器,不断侦听来自客户端的连接请求,接收客户端发来的对应那个“方法”的名称、参数类型、参数值,根据这些信息找到相关方法,反射机制执行,将执行结果作为响应返回给客户端。
方法池
方法池以接口名称为键,以RMIDefinition为值的Map,RMIDefinition类封装了被调用方法所在类的klass和object,代码片段如下:
public class RMIDefinition {
private Class<?> klass;
private Object object;
XML配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<RmiMapping>
<mapping interface = "com.mec.rmi.test.IAppInterface"
class = "com.mec.rmi.test.AppInterfaceImpl">
</mapping>
</RmiMapping>
RMIFactory扫描配置文件:
public class RMIFactory {
private static final Map<String, RMIDefinition> rmiPool =
new HashMap<String, RMIDefinition>();
public RMIFactory() {
}
public static void scanRmiMapping(String mappingPath) {
new XMLParser() {
@Override
public void dealElement(Element element, int index) {
String interfaceName = element.getAttribute("interface");
String className = element.getAttribute("class");
try {
Class<?> klass = Class.forName(className);
Object object = klass.newInstance();
RMIDefinition rmiDefinition = new RMIDefinition(klass, object);
rmiPool.put(interfaceName, rmiDefinition);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}.parseTag(XMLParser.getDocument(mappingPath), "mapping");;
}
public static RMIDefinition getRmiDefinition(String interfaceName) {
return rmiPool.get(interfaceName);
}
}
RMI客户端
客户端主要对外提供获取代理对象的方法,方法名称和参数类型与参数值,json序列化发送给服务器端。
public <T> T getProxy(Class<?> klass) {
MecProxy mecProxy = new MecProxy();
IMethodInvoker methodInvoker = new IMethodInvoker() {
@SuppressWarnings({ "hiding"})
@Override
public <T> T methodInvoke(Object object, Method method, Object[] args) throws ConnectServerFailureException {
Socket socket = null;
DataInputStream dis = null;
DataOutputStream dos = null;
try {
socket = new Socket(rmiServerIp, rmiServerPort);
dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(method.toString());
dos.writeUTF(getArgs(args));
dis = new DataInputStream(socket.getInputStream());
String resultString = dis.readUTF();
Type type = method.getGenericReturnType();
return ArgumentMaker.gson.fromJson(resultString, type);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
close(dis, dos, socket);
}
return null;
}
};
mecProxy.setMethodInvoke(methodInvoker);
return mecProxy.getProxy(klass);
}
private String getArgs(Object[] args) {
if (args == null || args.length <= 0) {
return "";
}
ArgumentMaker maker = new ArgumentMaker();
for (int i = 0; i < args.length; i++) {
maker.add("arg" + i, args[i]);
}
return maker.toString();
}
RMI服务器
服务器负责侦听客户端的请求,侦听到一个客户端的请求之后开一个线程去处理这个请求,即找到这个方法并反射机制执行,将返回值会送给客户端。
public class RMIServer implements Runnable {
private ServerSocket server;
private int port;
private volatile boolean goon;
public RMIServer() {
}
public void setPort(int port) {
this.port = port;
}
public void startup() {
if (goon) {
return;
}
try {
server = new ServerSocket(port);
this.goon = true;
new Thread(this, "RMI Server").start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void shutdown() {
if (goon == false) {
return;
}
close();
}
private void close() {
this.goon = false;
try {
if (server != null && !server.isClosed()) {
server.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
server = null;
}
}
@Override
public void run() {
while (goon) {
try {
Socket socket = server.accept();
new RMIService(socket);
} catch (IOException e) {
if (goon) {
goon = false;
}
}
}
}
}
public class RMIService implements Runnable {
private Socket socket;
public RMIService() {
}
public RMIService(Socket socket) {
this.socket = socket;
new Thread(this, "RMI Service").start();
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
String methodString = dis.readUTF();
String argsString = dis.readUTF();
Object result = invokeMethod(methodString, argsString);
dos.writeUTF(ArgumentMaker.gson.toJson(result));
dis.close();
dos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private Object invokeMethod(String methodString, String argsString) {
Object result = null;
MethodInformation methodInfo = new MethodInformation(methodString);
String interfaceName = methodInfo.getClassName();
RMIDefinition rmiDefinition = RMIFactory.getRmiDefinition(interfaceName);
Class<?> klass = rmiDefinition.getKlass();
Object object = rmiDefinition.getObject();
try {
Method method = methodInfo.getMethod(klass);
result = method.invoke(object, getParaValues(method, argsString));
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Object[] getParaValues(Method method, String argsString) {
Object[] paraValues = null;
Parameter[] paras = method.getParameters();
int paraCount = paras.length;
if (paraCount <= 0) {
return new Object[] {};
}
paraValues = new Object[paraCount];
ArgumentMaker maker = new ArgumentMaker(argsString);
for (int i = 0; i < paraCount; i++) {
paraValues[i] = maker.getValue("arg" + i, paras[i].getParameterizedType());
}
return paraValues;
}
}
方法信息
MethodInformation是一个将方法名称,方法所在类对应的接口名称和该方法的参数的类型名称,作为成员,并对服务器找到方法以及反射机制执行方法提供一些“对外接口”。
public class MethodInformation {
private String className;
private String methodName;
private String[] paraTypeName;
MethodInformation() {
}
MethodInformation(String methodString) {
parse(methodString);
}
String getClassName() {
return className;
}
String getMethodName() {
return methodName;
}
String[] getParaTypeName() {
return paraTypeName;
}
MethodInformation parse(String methodString) {
String[] methodInfoString = methodString.split(" ");
String methodInfo = methodInfoString[methodInfoString.length - 1];
int leftBracketIndex = methodInfo.indexOf('(');
String methodFullName = methodInfo.substring(0, leftBracketIndex);
int lastDotIndex = methodFullName.lastIndexOf('.');
this.className = methodFullName.substring(0, lastDotIndex);
this.methodName = methodFullName.substring(lastDotIndex + 1);
String paraTypes = methodInfo.substring(leftBracketIndex + 1, methodInfo.length() - 1);
parseParaTypes(paraTypes);
return this;
}
void parseParaTypes(String paraTypes) {
if (paraTypes.length() <= 0) {
paraTypeName = new String[] {};
return;
}
paraTypeName = paraTypes.split(",");
}
Method getMethod(Class<?> klass) throws NoSuchMethodException, SecurityException {
Method method = klass.getDeclaredMethod(methodName, getParaTypes(klass));
return method;
}
Class<?>[] getParaTypes(Class<?> klass) {
int paraCount = paraTypeName.length;
if (paraCount <= 0) {
return new Class<?>[] {};
}
Class<?>[] paraTypes = new Class<?>[paraCount];
for (int i = 0; i < paraCount; i++) {
try {
Class<?> paraType = Class.forName(paraTypeName[i]);
paraTypes[i] = paraType;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return paraTypes;
}
}
以上模拟RMI技术的核心:
1.代理;
2.反射机制;
3.网络传递参数;
4.网络传递返回值;
5.json序列化
6.短链接