详解 RMI技术 的基本实现

Youzg LOGO

RMI机制 作为我们现在的 短链接的基础,也是非常重要的
并且最重要的是,在未来的 分布式
微服务架构中,RPC技术起到了决定性作用
而本篇博文的主题 —— RMI技术,就是
RPC技术的一个 Java版的缩影
相信在未来的学习成长中,同学们会领略到
RMI技术强悍之处

那么,话不多说,现在就开始本篇博文的讲解吧:

定义:

Remote Methed Invoke,
即:远程方法调用
是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象方法

下面,本人来通过一张图,来展示下RMI技术的使用:
RMI 展示


在本人《C/SFramework》专栏中,存在着大量客户端向服务器端发出的请求
而这个请求到了服务器端,实质上是要执行一些服务器端的方法,并得到一个“响应”
那么,如果把请求当成一个“本地方法”,
客户端执行,而实质上,该方法只在服务器端存在真正的本体

那么,使用RMI技术,有什么好处呢?

好处:

分布式Java应用程序之间的远程通信提供服务,
即:提供分布式服务


现在,本人就来讲解下
RMI技术的核心

  1. 代理技术(也就是本人之前博文中所讲解的MecProxy小工具)
  2. 反射机制(用于调用对端的方法);
  3. 网络技术(用于传输“请求”与“结果”);
  4. 短连接(RMI技术的主要意义)

那么,说了这么多,本人怎么可能不实现下呢?
首先,让我们思考下 该如何实现?

实现原理:

  • 客户端使用代理执行相关方法
  • 服务器端存在具体被执行的方法

那么,接下来,我们来具体实现下上述结论:

现在,本人来提供 五个工具类

工具类:

Class类型转换器 —— TypeConverter:

请观看本人博文 —— 《【小工具】Class类型转换器 —— TypeConverter》


参数序列化/反序列化器 —— ArgumentMaker:

请观看本人博文 —— 《【小工具】参数序列化/反序列化器 —— ArgumentMaker》


Properties解析器 —— PropertiesParser:

请观看本人博文 —— 《【小工具】Properties解析器 —— PropertiesParser》


XML解析器 —— XMLParser:

请观看本人博文 —— 《【小工具】XML解析器 —— XMLParser》


代理建造器 —— ProxyBuilder:

请观看本人博文 —— 《【小工具】代理建造器 —— ProxyBuilder》


现在,本人来说明下 所需的 Jar包支持

Jar包 支持:

  • cglib-nodep
  • gson
  • fastjson

(三者 均为 代理建造器 小工具 需要)


RMI工厂(核心):

基本思路

将从 配置文件 中 读取到的 RMI映射关系
存储到 一个Pool中,以便我们之后远程方法调用时的需要

代码实现
首先,为了我们能够处理 客户端发来的“请求”,
本人来给出三个类:
封装要执行的方法的信息MethodInformation类

MethodInformation类:

package edu.youzg.rmi_impl.core;

import edu.youzg.util.TypeConverter;

import java.lang.reflect.Method;

/**
 * 记录 客户端调用方法 的“全部信息”
 */
public class MethodInformation {
    private String className;
    private String methodName;
    private String[] strParameterTypes;

    public MethodInformation() {
    }

    public MethodInformation(String methodInfo) {
        this.parseString(methodInfo);
    }

    /**
     * 获取 该方法的 参数类型数组
     *
     * @return 该方法的 参数类型数组
     * @throws ClassNotFoundException
     */
    private Class<?>[] getParameterTypes() throws ClassNotFoundException {
        int cnt = this.strParameterTypes.length;
        if (cnt <= 0) {
            return new Class<?>[]{};
        }
        Class<?>[] res = new Class<?>[cnt];
        for (int i = 0; i < cnt; i++) {
            res[i] = TypeConverter.toType(strParameterTypes[i]);
        }

        return res;
    }

    /**
     * 获取该方法 的 Method对象<br/>便于反射调用
     *
     * @param klass
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws SecurityException
     */
    public Method getMethod(Class<?> klass)
            throws ClassNotFoundException, NoSuchMethodException, SecurityException {
        Class<?>[] paraTypes = getParameterTypes();
        Method method = klass.getDeclaredMethod(methodName, paraTypes);
        return method;
    }

    /**
     * 从拆分出来的 String数组 中,挑出 方法的签名
     *
     * @param strs
     * @return
     */
    private String getMethodString(String[] strs) {
        for (int i = 0; i < strs.length; i++) {
            if (strs[i].endsWith(")")) {
                return strs[i];
            }
        }

        return null;
    }

    /**
     * 将整个参数字符串,分割成 一个个的 String类型参数 数组
     *
     * @param parameterTypesStr
     */
    private void parseParameterString(String parameterTypesStr) {
        if (parameterTypesStr.length() <= 0) {
            this.strParameterTypes = new String[]{}; // 使其不为null,便于后续步骤中当作参数使用
            return;
        }
        this.strParameterTypes = parameterTypesStr.split(",");
    }

    /**
     * 将给定的字符串 解析为 该变量的成员属性
     *
     * @param methodInfo
     * @return 该变量本身,便于链式调用
     */
    public MethodInformation parseString(String methodInfo) {
        // “方法字符串”的构成 —— 类全路径名.方法名(参数字符串)
        String[] strs = methodInfo.split(" ");

        String methodString = getMethodString(strs);
        int leftBracketIndex = methodString.indexOf("(");
        String methodFullName = methodString.substring(0, leftBracketIndex);

        int lastDotIndex = methodFullName.lastIndexOf(".");
        this.className = methodFullName.substring(0, lastDotIndex);
        this.methodName = methodFullName.substring(lastDotIndex + 1);

        String parameterTypesStr = methodString.substring(leftBracketIndex + 1, methodString.length() - 1);
        parseParameterString(parameterTypesStr);

        return this;
    }

    public String getClassName() {
        return this.className;
    }

    public String getMethodName() {
        return this.methodName;
    }

    public String[] getStrParameterTypes() {
        return this.strParameterTypes;
    }

}

接下来是用于封装 通过反射机制执行指定方法 所需的类与对象 的类:

RMIDefinition 类:

package edu.youzg.rmi_impl.core;

/**
 * RMI方法信息:<br/>
 * 用于保存一个类的 Class对象 和 一个实体类对象<br/>
 * 便于我们之后的调用
 */
public class RMIDefinition {
    private Class<?> klass;
    private Object object;

    public RMIDefinition() {
    }

    public RMIDefinition(Class<?> klass, Object object) {
        this.klass = klass;
        this.object = object;
    }

    public Class<?> getKlass() {
        return klass;
    }

    public void setKlass(Class<?> klass) {
        this.klass = klass;
    }

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

}

最后是通过扫描配置文件形成映射关系RMIFactory类

RMIFactory类:

package edu.youzg.rmi_impl.core;

import edu.youzg.util.XMLParser;
import edu.youzg.util.exceptions.XMLIsInexistentException;

import org.w3c.dom.Element;

import java.util.HashMap;
import java.util.Map;

/**
 * 扫描指定的配置文件,<br/>
 * 并将映射关系,存储到一个map中
 */
public class RMIFactory {
    private static final Map<String, RMIDefinition> rmiPool
            = new HashMap<>();

    public RMIFactory() {
    }

    /**
     * 扫描指定的配置文件,<br/>
     * 并将映射关系,存储到一个map中
     * @param xmlPath 目标xml文件的路径
     */
    public static void scanRMIMapping(String xmlPath) {
        try {
			new XMLParser() {

			    @Override
			    public void dealElement(Element element, int index) {
			        String interfaceStr = element.getAttribute("interface");
			        String classStr = element.getAttribute("class");

			        try {
			            Class<?> klass = Class.forName(classStr);
			            Object object = klass.newInstance();

			            RMIDefinition rmiDefinition = new RMIDefinition(klass, object);

			            rmiPool.put(interfaceStr, rmiDefinition);
			        } catch (ClassNotFoundException e) {
			            e.printStackTrace();
			        } catch (IllegalAccessException e) {
			            e.printStackTrace();
			        } catch (InstantiationException e) {
			            e.printStackTrace();
			        }
			    }
			}.parseTag(XMLParser.getDocument(xmlPath), "mapping");
		} catch (XMLIsInexistentException e) {
			e.printStackTrace();
		}

    }

    /**
     * 通过接口名,查询对应的RMIDefinition
     * @param interfaceName 目标接口名
     * @return 该接口 对应的 RMIDefinition
     */
    public static RMIDefinition getRmiDefinition(String interfaceName) {
        return rmiPool.get(interfaceName);
    }

}

接下来,我们来编写 客户端

客户端:

基本思路

  1. 客户端需要连接RMI服务器;
  2. 向服务器发送要执行的方法的名称、参数类型、参数值;
  3. 等待服务器返回执行执行结果,
    这个结果可以看成“响应”

实现代码

自定义异常 —— ConnectServerFailureException 类:

package edu.youzg.util.rmi_impl.exceptions;

/**
 * 连接RMI服务器失败 异常
 */
public class ConnectServerFailureException extends Exception {
    private static final long serialVersionUID = -4943840549663693585L;

    public ConnectServerFailureException() {
    }

    public ConnectServerFailureException(String message) {
        super(message);
    }

    public ConnectServerFailureException(Throwable cause) {
        super(cause);
    }

    public ConnectServerFailureException(String message, Throwable cause) {
        super(message, cause);
    }

    public ConnectServerFailureException(String message, Throwable cause, boolean enableSuppression,
                                         boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

}

RMI 代理处理逻辑 —— RMI方法执行器:

package edu.youzg.rmi_impl.client;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.Socket;

import com.alibaba.fastjson.JSON;

import edu.youzg.rmi_impl.exceptions.ConnectServerFailureException;
import edu.youzg.util.ArgumentMaker;
import edu.youzg.util.proxy.IMethodInvoker;

/**
 * RMI方法执行器
 */
public class RMIMethodInvoker implements IMethodInvoker {
    private String rmiServerIp;
    private int rmiServerPort;

    public RMIMethodInvoker() {
    }

    /**
         * 将所有参数,都转换为:<br/>以argi为键,<br/>参数值的json字符串 为值 <br/>的map 的json字符串
     *
     * @param args 目标 参数数组
     * @return 相应的 json字符串
     */
    private String getArgs(Object[] args) {
        if (args == null || args.length <= 0) {
            return "";
        }
        ArgumentMaker argumentMaker = new ArgumentMaker();
        for (int i = 0; i < args.length; i++) {
            argumentMaker.add("arg" + i, args[i]);
        }
        return argumentMaker.toString();
    }


    @Override
    public <T> T methodInvoke(Object object, Method method, Object[] args) throws ConnectServerFailureException {
        Socket socket = null;
        DataInputStream dis = null;
        DataOutputStream dos = null;
        boolean ok = true;

        String ip = null;
        int port = 6666;

        try {
            ip = this.rmiServerIp;
            port = this.rmiServerPort;
            socket = new Socket(ip, port);
            
            /*
	                * 发送method 的字符串
	                * 发送参数数组的map化json
            **/
            dos = new DataOutputStream(socket.getOutputStream());
            dos.writeUTF(method.toString());
            dos.writeUTF(getArgs(args));

            /*
                       * 接收 结果json
            **/
            dis = new DataInputStream(socket.getInputStream());
            String resultStr = dis.readUTF();

            /*
                       * 转换结果为目标类型,并返回
             **/
            Type returnType = method.getGenericReturnType();
            
            T result = null;
            if (returnType.toString().equals("void")) {	// 若是void类型,gson无法转换,交由fastjson转换
            	result = JSON.parseObject(resultStr, returnType);
            } else {	// 若不是void类型,为了避免fastjson无法转换集合,交由gson转换
            	result = ArgumentMaker.gson.fromJson(resultStr, returnType);
            }
            
            return result;
        } catch (IOException e) {
           ok = false;
        } finally { //无论如何,释放资源
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null && !socket.isClosed()) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        if (!ok) {
        	throw new ConnectServerFailureException("连接RMI服务器失败!");
        }

        return null;
    }

    public String getRmiServerIp() {
        return rmiServerIp;
    }

    public void setRmiServerIp(String rmiServerIp) {
        this.rmiServerIp = rmiServerIp;
    }

    public int getRmiServerPort() {
        return rmiServerPort;
    }

    public void setRmiServerPort(int rmiServerPort) {
        this.rmiServerPort = rmiServerPort;
    }

}

RMI客户端 —— RMIClient 类:

package edu.youzg.rmi_impl.client;

import edu.youzg.util.PropertiesParser;
import edu.youzg.util.proxy.ProxyBuilder;

/**
 * RMI客户端
 */
public class RMIClient {
    private RMIMethodInvoker methodInvoker;

    public RMIClient() {
        this.methodInvoker
                = new RMIMethodInvoker();
    }

    public RMIClient(RMIMethodInvoker methodInvoker) {
        this.methodInvoker = methodInvoker;
    }

    public void initRmiClient(String configFilePath) {
        PropertiesParser.loadProperties(configFilePath);
        String rmiPortStr = PropertiesParser.value("rmiServerPort");
        String rmiIpStr = PropertiesParser.value("rmiServerIp");
        if (rmiIpStr.length() > 0) {
            this.methodInvoker.setRmiServerIp(rmiIpStr);
        }
        if (rmiPortStr.length() > 0) {
            this.methodInvoker.setRmiServerPort(Integer.valueOf(rmiPortStr));
        }
    }

    public void setRmiServerIp(String rmiServerIp) {
        this.methodInvoker.setRmiServerIp(rmiServerIp);
    }

    public void setRmiServerPort(int rmiServerPort) {
        this.methodInvoker.setRmiServerPort(Integer.valueOf(rmiServerPort));
    }

    // 由于在“RMI机制”中,无论是jdk代理,还是cglib代理,都可以使用
    // 为了省去导jar包的麻烦,以及效率问题,本人在此处,就使用jdk提供的代理
    /**
     * 获取代理对象
     * @param klass
     * @param <T>
     * @return
     */
    public <T> T getProxy(Class<?> klass) {
        ProxyBuilder builder = new ProxyBuilder();
        builder.setMethodInvoker(this.methodInvoker);
        return builder.creatProxy(klass);
    }

}

那么,客户端我们处理好了,现在我们来思考下服务器端该如何编写:

服务器端:

基本思路

服务器端必须能够建立服务器
而且不断侦听来自客户端的连接请求;
这个连接请求实质上是客户端发出请求的第一步
接着,服务器开始接收客户端发来的,对应那个“方法”的名称参数类型参数值
服务器根据客户端发来的上述信息,找到相关方法,并执行
最后,将执行结果返回给客户端

实现代码

那么,现在我们来实现下RMI服务器端的代码:

RMIServer类:

package edu.youzg.rmi_impl.server;

import edu.youzg.util.PropertiesParser;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * RMI服务器端
 */
public class RMIServer implements Runnable {
    private static final int CORE_THREAD_COUNT = 20;
    private static final int MAX_THREAD_COUNT = 180;
    private static final long ALIVE_TIME = 5000;

    private int rmiPort;
    private ServerSocket server;
    private volatile boolean startUp;
    private ThreadPoolExecutor threadPool;
    private int coreThreadCount;
    private int maxThreadCount;
    private long aliveTime;

    public RMIServer() {
        this.coreThreadCount = CORE_THREAD_COUNT;
        this.maxThreadCount = MAX_THREAD_COUNT;
        this.aliveTime = ALIVE_TIME;
    }

    public void setRmiPort(int rmiPort) {
        this.rmiPort = rmiPort;
    }

    public void setCoreThreadCount(int coreThreadCount) {
        this.coreThreadCount = coreThreadCount;
    }

    public void setMaxThreadCount(int maxThreadCount) {
        this.maxThreadCount = maxThreadCount;
    }

    public void setAliveTime(long aliveTime) {
        this.aliveTime = aliveTime;
    }

    public void initRmiServer(String configFilePath) {
        PropertiesParser.loadProperties(configFilePath);

        String rmiPortStr = PropertiesParser.value("rmiServerPort");
        if (rmiPortStr.length() > 0) {
            this.rmiPort = Integer.valueOf(rmiPortStr);
        }

        String poolCoreThreadCount = PropertiesParser.value("coreThreadCount");
        if (poolCoreThreadCount.length() > 0) {
            this.coreThreadCount = Integer.valueOf(poolCoreThreadCount);
        }

        String poolMaxThreadCount = PropertiesParser.value("maxThreadCount");
        if (poolMaxThreadCount.length() > 0) {
            this.maxThreadCount = Integer.valueOf(poolMaxThreadCount);
        }

        String poolAliveTime = PropertiesParser.value("aliveTime");
        if (poolAliveTime.length() > 0) {
            this.aliveTime = Integer.valueOf(poolAliveTime);
        }
    }

    public void startUp() {
        if (startUp) {
            return;
        }

        try {
            this.threadPool = new ThreadPoolExecutor(
                    coreThreadCount,
                    maxThreadCount,
                    aliveTime,
                    TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>());
            this.server = new ServerSocket(this.rmiPort);
            this.startUp = true;
            new Thread(this, "RMI Server").start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void close() {
        this.startUp = false;
        if (this.server == null || this.server.isClosed()) {
            return;
        }

        try {
            this.server.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            this.server = null;
        }
    }

    public void shutdown() {
        if (!startUp) {
            return;
        }
        close();
        this.threadPool.shutdown();
    }

    @Override
    public void run() {
        while (startUp) {
            try {
                Socket client = this.server.accept();
                threadPool.execute(new RMIService(client));
            } catch (IOException e) {
                if (this.startUp == true) {
                    this.startUp = false;
                }
            }
        }
    }
}

那么,现在本人来编写下上段代码中,使用的服务器端处理客户端请求的线程类

RMIService类:

package edu.youzg.rmi_impl.server;

import com.alibaba.fastjson.JSON;
import edu.youzg.rmi_impl.core.MethodInformation;
import edu.youzg.rmi_impl.core.RMIDefinition;
import edu.youzg.rmi_impl.core.RMIFactory;
import edu.youzg.util.ArgumentMaker;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.Socket;

public class RMIService implements Runnable {
    private Socket socket;

    public RMIService(Socket socket) {
        this.socket = socket;
    }

    /**
     * 解析字符串,获取 参数的值
     * @param method
     * @param argString
     * @return
     */
    private Object[] getParameterValues(Method method, String argString) {
        Object[] res = null;

        Parameter[] parameters = method.getParameters();
        int cnt = parameters.length;

        if (cnt <= 0) {
            return new Object[] {};
        }
        res = new Object[cnt];

        ArgumentMaker argumentMaker = new ArgumentMaker(argString);
        for (int i = 0; i < cnt; i++) {
            res[i] = argumentMaker.getValue("arg" + i, parameters[i].getParameterizedType());	// getParameterizedType 带泛型
        }

        return res;
    }

    /**
     * 调用该方法
     * @param methodInfo
     * @param argStr
     * @return
     */
    private Object invokeMethod(String methodInfo, String argStr) {
        MethodInformation methodInfomation = new MethodInformation()
                .parseString(methodInfo);
        String interfaceName = methodInfomation.getClassName(); // RMI客户端传过来的参数 是 接口名称
        RMIDefinition rmiDefinition = RMIFactory.getRmiDefinition(interfaceName);
        Class<?> klass = rmiDefinition.getKlass();
        Object object = rmiDefinition.getObject();

        Object result = null;
        try {
            Method method = methodInfomation.getMethod(klass);
            Object[] values = getParameterValues(method, argStr);
            result = method.invoke(object, values);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    public void run() {
        try {
            DataInputStream dis = new DataInputStream(this.socket.getInputStream());
            String methodInfo = dis.readUTF();
            String argStr = dis.readUTF();

            DataOutputStream dos = new DataOutputStream(this.socket.getOutputStream());
            Object result = null;
            result = invokeMethod(methodInfo, argStr);
            
            dos.writeUTF(JSON.toJSONString(result));

            dis.close();
            dos.close();
            this.socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

那么,现在我们来测试下上面的代码成功不成功:

测试:

首先,本人来给出一个用于保存信息的类:

package edu.youzg.test;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-15 17:41
 * @Description: 带你深究Java的本质!
 */
public class User {
    private String name;
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "用户" + this.name + "今年" + this.age + "岁了!";
    }

}

现在,本人来给出一个dao层接口:

package edu.youzg.test;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-15 17:43
 * @Description: 带你深究Java的本质!
 */
public interface IUserDao {
    void login(String name, int age);
}

接着,本人再来给出这个接口的实现类:

package edu.youzg.test;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-15 17:44
 * @Description: 带你深究Java的本质!
 */
public class UserDaoImpl implements IUserDao {

    @Override
    public void login(String name, int age) {
        System.out.println("登录功能被调用");
        System.out.println(new User(name, age));
    }

}

相应地,本人来给出一个配置文件:

配置文件:

客户端连接初始化 配置文件:

rmiServerIp=localhost
rmiServerPort=6666

服务器端初始化 配置文件:

rmiServerPort=6666
coreThreadCount=25
maxThreadCount=200
aliveTime=3000

RMI映射文件:

<?xml version="1.0" encoding="UTF-8"?>
<RmiMapping>
	<mapping interface="edu.youzg.test.IUserDao"
	class="edu.youzg.test.UserDaoImpl"></mapping>
</RmiMapping>

那么,现在我们来通过一个测试类来看看运行结果:

测试类:

服务器端:

package edu.youzg.test;

import edu.youzg.rmiImpl.core.RMIFactory;
import edu.youzg.rmiImpl.server.RMIServer;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-15 17:45
 * @Description: 带你深究Java的本质!
 */
public class RSTest {

    public static void main(String[] args) {
        RMIFactory.scanRMIMapping("/resource/RMIMapping.xml");
        RMIServer server = new RMIServer();
        server.initRmiServer("/resource/rmiServerConfig.properties");
        server.startUp();
    }

}

客户端:

package edu.youzg.test;

import edu.youzg.rmiImpl.client.RMIClient;

/**
 * @Author: Youzg
 * @CreateTime: 2020-07-15 17:45
 * @Description: 带你深究Java的本质!
 */
public class RCTest {

    public static void main(String[] args) {
        RMIClient client = new RMIClient();
        client.initRmiClient("/resource/rmiClientConfig.properties");

        IUserDao dao = client.getProxy(IUserDao.class);    //获取该接口的代理对象

        //通过代理对象“执行该方法”:
        //其本质上是将方法所需信息传输给服务器端,
        //服务器端从配置文件中找到该接口相应的实现类,
        //执行该实现类的具体方法,并将返回值返回给客户端
        dao.login("youzg", 6666);
        System.out.println("登录成功!");
    }

}

目录结构

目录结构 展示


现在,本人来展示下运行结果

运行结果:

服务器端
服务器端
客户端
客户端

那么,可以看到:
我们基本上实现了RMI技术!


若有需要上述源码的同学,本人已将本文所讲解到的代码打成了Jar包:

工具 Jar包:

如有需要,请点击下方链接:
Youzg-RMI-Impl


心得体会 与 未来展望:

RMI技术可以说是RPC技术基础
如下图所示,RPC技术基本原理
RPC 展示
从上图中我们能够看得出二者之间的联系!

在我们之后的学习中,分布式、微服务等架构模式,都会采用到RPC技术
因此,在本篇博文中,希望同学们能够对此加以重视!

posted @ 2020-04-27 09:52  在下右转,有何贵干  阅读(246)  评论(0编辑  收藏  举报