一、RMI 定义

RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。
Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
远程过程调用(Remote Procedure Call, RPC)可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯。

二、RMI 通信模型

从方法调用角度来看,RMI 要解决的问题,是让客户端对远程方法的调用可以相当于对本地方法的调用而屏蔽其中关于远程通信的的内容,即使在远程上,也和在本地上是一样的。
从客户端-服务器模型来看,客户端程序直接调用服务端,两者之间是通过 JRMP 协议通信,这个协议类似与http协议,规定了客户端和服务端通信要满足的规范。
但是,实际上 客户端只与代表远程主机中对象的 Stub 对象进行通信,丝毫不知道Server的存在。客户端只是调用Stub对象中的本地方法,Stub 对象是一个本地对象,它实现了远程对象向外暴露的接口,也就是说它的方法和远程对象暴露的方法签名是相同的。客户端认为它是调用远程对象的方法,实际上是调用 Stub对象中的方法。可以理解为 Stub 对象是远程对象在本地的一个代理,当客户端调用方法的时候,Stub 对象会将调用通过网络传递给远程对象。
在java 1.2之前,与Stub对象直接对话的是Skeleton对象,在Stub对象将调用传递给Skeleton的过程中,其实这个过程是通过JRMP协议实现转化的,通过这个协议将调用从一个虚拟机转到另一个虚拟机。在Java 1.2之后,与Stub对象直接对话的是Server程序,不再是Skeleton对象了。
所以从逻辑上来看,数据是在Client和Server之间横向流动的,但是实际上是从Client到Stub,然后从Skeleton到Server这样纵向流动的。

三、重要问题

1.数据的传递问题

我们都知道在 Java 程序中引用类型的参数传递是按引用传递的,对于在同一个虚拟机中的传递是没有任何问题的,因为参数的引用对应的是同一个内存空间,但是对于分布式系统中,由于对象不再存在于同一个内存空间,虚拟机A的对象引用对于虚拟机B没有任何意义,那么怎么解决这个问题呢?
第一种:将引用传递更改为值传递,也就是将对象序列化为字节,然后使用该字节的副本在客户端和服务器之间传递,而且一个虚拟机中对改值的修改不会影响到其他主机中的数据;但对象的序列化也有一个问题,就是对象的嵌套引用就会造成序列化的嵌套,这必然会导致数据量的激增,因此我们需要有选择进行序列化,在 Java 中一个对象如果能够被序列化,需要满足下面两个条件之一:
是 Java 基本类型;
实现了Serializable 接口。
对于容器类,如果其中的对象是可以序列化的,那么该容器也是可以序列化的。
可序列化的子类也是可以序列化的。

第二种:仍然使用引用传递,每当远程主机调用本地方法时,该调用还要通过本地主机查询该引用对应的对象,在任何一台机器上的改变都会影响原始主机上的数据,因为这个对象是共享的。

RMI 中的参数传递和结果返回可以使用的三种机制(取决于数据类型):
简单类型:按值传递,直接传递数据拷贝。
远程对象引用(实现了Remote接口):以远程对象的引用传递。
远程对象引用(未实现Remote接口):按值传递,通过序列化对象传递副本,本身不允许序列化的对象传递给远程方法。

2.远程对象的发现问题

在调用远程对象的方法之前需要一个远程对象的引用,如何获得这个远程对象的引用在 RMI 中是一个关键的问题。如果将远程对象的发现类比与IP地址的发现可能比较好理解。
在日常使用网络时,基本上都是通过域名来定位一个网站,但是实际上网络是通过 IP 地址来定位网站的,因此其中就需要一个映射的过程,域名系统(DNS)就是为了这个目的出现的,在域名系统中通过域名来查找对应的IP地址来访问对应的服务器。那么对应的,IP地址在这里就相当于远程对象的引用,而DNS则相当于一个注册表(Registry)。而域名在 RMI中就相当于远程对象的标识符,客户端通过提供远程对象的标识符访问注册表,来得到远程对象的引用。这个标识符是类似与 URL 地址格式的,它要满足的规范如下:
该名称是URL形式的,类似于 http 的 URL,schema 是 rmi:
格式类似与 rmi://host:port/name,host指明注册表运行的主机,port表明接收调用的端口,name是一个标识该对象的简单名称。主机和端口都是可选的,如果省略了主机,则默认运行在本地,如果端口也省略,则默认端口1099。

四、RMI 工作原理

image.png
image.png
RMI 工作原理:
方法调用从客户对象经占位程序(Stub)、远程引用层(RRL)和传输层 向下,传递给主机,然后再次经 传输层,向上穿过 远程调用层和骨干网,到达服务器对象。
占位程序(Stub)扮演着远程服务器对象的代理角色,使该对象可被客户激活。
远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。
传输层管理实际的连接,并且追踪可以接受方法调用的远程对象。
服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。
返回值向下经远程引用层、服务器端的传输层传递回客户端,在向上经传输层和远程调用层返回。
最后,占位程序获得返回值。

要完成以上步骤需要有以下几步:
1.生成一个远程接口
2.实现远程对象(服务器端程序)
3.生成占位程序和骨干网(服务器端程序)
4.编写服务器程序
5.编写客户程序
6.注册远程对象
7.启动远程对象

RMI - 图3

五、服务端启动

1.创建注册表 Registry

  1. LocateRegistry.createRegistry(registryPort);
  2. public static Registry createRegistry(int port) throws RemoteException {
  3. return new RegistryImpl(port);
  4. }

1.1.创建注册表

  1. public RegistryImpl(final int var1) throws RemoteException {
  2. this.bindings = new Hashtable(101);
  3. if (var1 == 1099 && System.getSecurityManager() != null) {
  4. try {
  5. AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
  6. public Void run() throws RemoteException {
  7. LiveRef var1x = new LiveRef(RegistryImpl.id, var1);
  8. RegistryImpl.this.setup(new UnicastServerRef(var1x, (var0) -> {
  9. return RegistryImpl.registryFilter(var0);
  10. }));
  11. return null;
  12. }
  13. }, (AccessControlContext)null, new SocketPermission("localhost:" + var1, "listen,accept"));
  14. } catch (PrivilegedActionException var3) {
  15. throw (RemoteException)var3.getException();
  16. }
  17. } else {
  18. //根据 端口号创建 liveRef 对象
  19. LiveRef var2 = new LiveRef(id, var1);
  20. //setUp()方法将指向正在初始化的RegistryImpl对象的远程引用ref(RemoteRef)
  21. //赋值为传入的UnicastServerRef对象,
  22. //这里涉及了向上转型。然后继续移交UnicastServerRef的exportObject()方法。
  23. this.setup(new UnicastServerRef(var2, RegistryImpl::registryFilter));
  24. }
  25. }

创建 LiveRef 对象:对象包含了传输层,TCP端口
image.png
对象liveRef向上转型为 UnicastServerRef
image.png
①:将正在创建的注册表RegistryImpl的远程引用赋值,调用UnicastServerRef对象的exportObject方法

  1. //setUp 将ResrtyImpl的远程引用ref 赋值为 UnicastServerRef 的 var1
  2. transient protected RemoteRef ref;
  3. private void setup(UnicastServerRef var1) throws RemoteException {
  4. this.ref = var1;
  5. //this指向当前对象 RegistryImpl
  6. var1.exportObject(this, (Object)null, true);
  7. }

使用代理创建Remote:为传入的远程引用对象RegistryImpl创建一个代理RegistryImpl_Stub,就是服务于客户端的Stub对象。

  1. public Remote exportObject(Remote var1, Object var2, boolean var3) throws RemoteException {
  2. //获取远程对象的class
  3. Class var4 = var1.getClass();
  4. Remote var5;
  5. try {
  6. //通过代理的方式创建ref引用
  7. var5 = Util.createProxy(var4, this.getClientRef(), this.forceStubUse);
  8. } catch (IllegalArgumentException var7) {
  9. throw new ExportException("remote object implements illegal remote interface", var7);
  10. }
  11. //如果创建出来的引用属于RemoteStub类及其子类,调用setSkeleton方法
  12. if (var5 instanceof RemoteStub) {
  13. //设置骨架
  14. //将 UnicastServerRef 对象的 skeleton 设置为 RegistryImpl
  15. this.setSkeleton(var1);
  16. }
  17. //根据传递进来的远程对象、当前类实例、当前ref引用的id以及传递的boolean参数来创建出Target实例,并再次传递到exportObject方法中
  18. Target var6 = new Target(var1, this, var5, this.ref.getObjID(), var3);
  19. this.ref.exportObject(var6);
  20. this.hashToMethod_Map = (Map)hashToMethod_Maps.get(var4);
  21. return var5;
  22. }

image.png

this.setSkeleton(var1);

Class var3 = Class.forName(var2, false, var1.getClassLoader());
return (Skeleton)var3.newInstance();

创建 RestryImpl_Skel 对象。

public void setSkeleton(Remote var1) throws RemoteException {
    if (!withoutSkeletons.containsKey(var1.getClass())) {
        try {
            //将 UnicastServerRef 对象的 skeleton 设置为 RegistryImpl
            this.skel = Util.createSkeleton(var1);
        } catch (SkeletonNotFoundException var3) {
            withoutSkeletons.put(var1.getClass(), (Object)null);
        }
    }
}

image.png
服务端创建的注册表 Registry
image.png
liveRef 导出对象:最终调用TCPEndpoint的exportObject把新建的target对象暴露出去

this.ref.exportObject(var6);

//sun.rmi.transport.tcp.TCPEndpoint#exportObject
public void exportObject(Target var1) throws RemoteException {
    this.transport.exportObject(var1);
}
//TCPEndpoint 传输层导出对象
public void exportObject(Target var1) throws RemoteException {
    synchronized(this) {
        //创建ServerSocket,监听客户端连接
        this.listen();
        ++this.exportCount;
    }

    boolean var2 = false;
    boolean var12 = false;

    try {
        var12 = true;
        super.exportObject(var1);
        var2 = true;
        var12 = false;
    } finally {
        if (var12) {
            if (!var2) {
                synchronized(this) {
                    this.decrementExportCount();
                }
            }

        }
    }

    if (!var2) {
        synchronized(this) {
            this.decrementExportCount();
        }
    }

}

TCPEndpoint#listen创建ServerSocket,监听客户端连接

private void listen() throws RemoteException {
    assert Thread.holdsLock(this);

    TCPEndpoint var1 = this.getEndpoint();
    int var2 = var1.getPort();
    if (this.server == null) {
        if (tcpLog.isLoggable(Log.BRIEF)) {
            tcpLog.log(Log.BRIEF, "(port " + var2 + ") create server socket");
        }

        try {
            //创建服务端 ServerSocket
            this.server = var1.newServerSocket();
            //创建线程
            Thread var3 = (Thread)AccessController.doPrivileged(new NewThreadAction(new TCPTransport.AcceptLoop(this.server), "TCP Accept-" + var2, true));
            //启动线程
            var3.start();
        } catch (BindException var4) {
            throw new ExportException("Port already in use: " + var2, var4);
        } catch (IOException var5) {
            throw new ExportException("Listen failed on port: " + var2, var5);
        }
    } else {
        SecurityManager var6 = System.getSecurityManager();
        if (var6 != null) {
            var6.checkListen(var2);
        }
    }

}

image.png
创建 RMI 接收线程:
image.png
接收客户端请求的线程启动,等待客户端请求接入:

//sun.rmi.transport.tcp.TCPTransport.AcceptLoop#run
public void run() {
    try {
        this.executeAcceptLoop();
    } finally {
        try {
            this.serverSocket.close();
        } catch (IOException var7) {
        }

    }

}

private void executeAcceptLoop() {
    if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
        TCPTransport.tcpLog.log(Log.BRIEF, "listening on port " + TCPTransport.this.getEndpoint().getPort());
    }

    while(true) {
        Socket var1 = null;

        try {
            var1 = this.serverSocket.accept();
            InetAddress var16 = var1.getInetAddress();
            String var3 = var16 != null ? var16.getHostAddress() : "0.0.0.0";

            try {
                TCPTransport.connectionThreadPool.execute(TCPTransport.this.new ConnectionHandler(var1, var3));
            } catch (RejectedExecutionException var11) {
                TCPTransport.closeSocket(var1);
                TCPTransport.tcpLog.log(Log.BRIEF, "rejected connection from " + var3);
            }
        } catch (Throwable var15) {
            Throwable var2 = var15;

            try {
                if (this.serverSocket.isClosed()) {
                    return;
                }

                try {
                    if (TCPTransport.tcpLog.isLoggable(Level.WARNING)) {
                        TCPTransport.tcpLog.log(Level.WARNING, "accept loop for " + this.serverSocket + " throws", var2);
                    }
                } catch (Throwable var13) {
                }
            } finally {
                if (var1 != null) {
                    TCPTransport.closeSocket(var1);
                }

            }

            if (!(var15 instanceof SecurityException)) {
                try {
                    TCPEndpoint.shedConnectionCaches();
                } catch (Throwable var12) {
                }
            }

            if (!(var15 instanceof Exception) && !(var15 instanceof OutOfMemoryError) && !(var15 instanceof NoClassDefFoundError)) {
                if (var15 instanceof Error) {
                    throw (Error)var15;
                }

                throw new UndeclaredThrowableException(var15);
            }

            if (!this.continueAfterAcceptFailure(var15)) {
                return;
            }
        }
    }
}

2.导出对象

2.1 创建服务端接口的代理对象

protected Object getProxyForService() {
    checkService();
    checkServiceInterface();

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.addInterface(getServiceInterface());

    if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null) {
        proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
    }
    if (this.interceptors != null) {
        AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
        for (Object interceptor : this.interceptors) {
            proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor));
        }
    }

    proxyFactory.setTarget(getService());
    proxyFactory.setOpaque(true);

    return proxyFactory.getProxy(getBeanClassLoader());
}

image.png

2.2 创建 RmiInvocationWrapper,设置导出对象

public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) {
    Assert.notNull(wrappedObject, "Object to wrap is required");
    Assert.notNull(rmiExporter, "RMI exporter is required");
    this.wrappedObject = wrappedObject;
    this.rmiExporter = rmiExporter;
}

image.png

3. UniCastRemoteObject.exportObject

 远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时, <br /> 该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”, <br /> 而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信, 而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。<br />根据端口创建 ServerRef:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/722839/1622711230726-afcabd88-47fb-4063-b717-6616fc37439f.png#clientId=u63317584-00f3-4&from=paste&height=181&id=u881a45d0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=361&originWidth=1422&originalType=binary&size=34990&status=done&style=none&taskId=u6af30203-7eae-4eac-be92-f81d7e88af8&width=711)
private static Remote exportObject(Remote obj, UnicastServerRef sref)
    throws RemoteException
    {
        // if obj extends UnicastRemoteObject, set its ref.
        if (obj instanceof UnicastRemoteObject) {
            ((UnicastRemoteObject) obj).ref = sref;
        }
        return sref.exportObject(obj, null, false);
    }

最终调用 远程引用 ref 的导出对象方法:
此时,Remote 的 Class 类型为:
image.png
创建代理类:
Proxy[RmiInvocationHandler,RemoteObjectInvocationHandler[UnicastRef [liveRef: [endpoint:192.168.66.37:0,objID:[-38e64d04:179d0bff3a3:-7fff, 3877812081421948771]]]]]
image.png

4.服务端绑定

bindings 类型为Hashtable,维护了一个 serviceName和Remote 的映射关系。

this.registry.rebind(this.serviceName, this.exportedObject);

//sun.rmi.registry.RegistryImpl#rebind
private Hashtable<String, Remote> bindings;
public void rebind(String var1, Remote var2) throws RemoteException, AccessException {
    this.bindings.put(var1, var2);
}

5.客户端启动

1.org.springframework.remoting.rmi.RmiClientInterceptor#lookupStub

protected Remote lookupStub() throws RemoteLookupFailureException {
    try {
        Remote stub = null;
        if (this.registryClientSocketFactory != null) {
            URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler());
            String protocol = url.getProtocol();
            if (protocol != null && !"rmi".equals(protocol)) {
                throw new MalformedURLException("Invalid URL scheme '" + protocol + "'");
            }
            String host = url.getHost();
            int port = url.getPort();
            String name = url.getPath();
            if (name != null && name.startsWith("/")) {
                name = name.substring(1);
            }
            Registry registry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory);
            stub = registry.lookup(name);
        }
        else {
            // Can proceed with standard RMI lookup API...
            stub = Naming.lookup(getServiceUrl());
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]");
        }
        return stub;
    }
    catch (MalformedURLException ex) {
        throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
    }
    catch (NotBoundException ex) {
        throw new RemoteLookupFailureException(
            "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
    }
    catch (RemoteException ex) {
        throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);
    }
}

1.获取 Stub 存根:

public static Remote lookup(String name)
        throws NotBoundException,
            java.net.MalformedURLException,
            RemoteException
    {
        ParsedNamingURL parsed = parseURL(name);
        Registry registry = getRegistry(parsed);

        if (parsed.name == null)
            return registry;
        return registry.lookup(parsed.name);
    }

1.1根据 serviceUrl 获取IP和port

image.png

1.2 获取注册表 Registry

image.png

1.3获取Stub 存根

public Remote lookup(String var1) throws AccessException, NotBoundException, RemoteException {
    try {
        //从ref引用的newCall方法获取RemoteCall实例
        //newCall()方法做的事情简单来看就是建立了跟远程RegistryImpl的Skeleton对象的连接
        StreamRemoteCall var2 = (StreamRemoteCall)this.ref.newCall(this, operations, 2, 4905912898345647071L);

        try {
            //从RemoteCall实例中获取输出流,将数据写入
            ObjectOutput var3 = var2.getOutputStream();
            var3.writeObject(var1);
        } catch (IOException var15) {
            throw new MarshalException("error marshalling arguments", var15);
        }
        //将RemoteCall实例传递给ref引用的invoke方法
        this.ref.invoke(var2);

        Remote var20;
        try {
            //获取输入流,读取传来的数据
            ObjectInput var4 = var2.getInputStream();
            var20 = (Remote)var4.readObject();
        } catch (IOException | ClassNotFoundException | ClassCastException var13) {
            var2.discardPendingRefs();
            throw new UnmarshalException("error unmarshalling return", var13);
        } finally {
            this.ref.done(var2);
        }

        return var20;
    } catch (RuntimeException var16) {
        throw var16;
    } catch (RemoteException var17) {
        throw var17;
    } catch (NotBoundException var18) {
        throw var18;
    } catch (Exception var19) {
        throw new UnexpectedException("undeclared checked exception", var19);
    }
}

获取一个TCPConnection

public RemoteCall newCall(RemoteObject var1, Operation[] var2, int var3, long var4) throws RemoteException {
    clientRefLog.log(Log.BRIEF, "get connection");
    //从ref引用对象中开启连接,获取一个TCP连接
    Connection var6 = this.ref.getChannel().newConnection();

    try {
        clientRefLog.log(Log.VERBOSE, "create call context");
        if (clientCallLog.isLoggable(Log.VERBOSE)) {
            this.logClientCall(var1, var2[var3]);
        }
        //创建 StreamRemoteCall 实例
        //将我们的服务信息(端口等)传递给StreamRemoteCall实例进行构建
        StreamRemoteCall var7 = new StreamRemoteCall(var6, this.ref.getObjID(), var3, var4);

        try {
            //传输定制化数据(默认无代码实现)
            this.marshalCustomCallData(var7.getOutputStream());
        } catch (IOException var9) {
            throw new MarshalException("error marshaling custom call data");
        }

        return var7;
    } catch (RemoteException var10) {
        //调用通信管道中的定时清理连接的方法,内部实现了定时任务调度,清理无用的连接
        this.ref.getChannel().free(var6, false);
        throw var10;
    }
}

2.RmiClientInterceptor 保存Stub

private Remote cachedStub;

if (this.cacheStub) {
    this.cachedStub = remoteObj;
}

image.png

3.创建服务接口代理

image.png

六、客户端发起调用

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-client-rmi.xml");

    //此处为代理对象,代理方式为 JDK 动态代理
    UserService myClient = context.getBean("myClient", UserService.class);
    User user = new User();
    user.setAge(30);
    User user1 = myClient.updateUser(user);
    System.out.println(user);
    System.out.println(user1);
}

1.执行代理类的RmiProxyFactoryBean的invoke,

RmiProxyFactoryBean继承RmiClientInterceptor

public Object invoke(MethodInvocation invocation) throws Throwable {
    //获取 Stub 存根,由于客户端启动时,Stub 缓存在 类型为Remote 的 cachedStub
    Remote stub = getStub();
    try {
        return doInvoke(invocation, stub);
    }
    catch (RemoteConnectFailureException ex) {
        return handleRemoteConnectFailure(invocation, ex);
    }
    catch (RemoteException ex) {
        if (isConnectFailure(ex)) {
            return handleRemoteConnectFailure(invocation, ex);
        }
        else {
            throw ex;
        }
    }
}

image.png

2.RmiClientInterceptor#doInvoke

protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
    throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
        return "RMI invoker proxy for service URL [" + getServiceUrl() + "]";
    }
    //
    return invocationHandler.invoke(createRemoteInvocation(methodInvocation));
}

2.1 创建远程调用

public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
    return new RemoteInvocation(methodInvocation);
}

public RemoteInvocation(MethodInvocation methodInvocation) {
    //方法名称
    this.methodName = methodInvocation.getMethod().getName();
    //参数类型
    this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
    //参数
    this.arguments = methodInvocation.getArguments();
}

2.3 Stub 类型为 RmiInvocationHandler,invoke 调用

//org.springframework.remoting.rmi.RmiInvocationWrapper#invoke
public Object invoke(RemoteInvocation invocation)
    throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    return this.rmiExporter.invoke(invocation, this.wrappedObject);
}

RmiExporter:
image.png
wrappedObject:
image.png

//org.springframework.remoting.rmi.RmiBasedExporter#invoke
protected Object invoke(RemoteInvocation invocation, Object targetObject)
    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    return super.invoke(invocation, targetObject);
}

//org.springframework.remoting.support.RemoteInvocationBasedExporter#invoke
protected Object invoke(RemoteInvocation invocation, Object targetObject)
    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    if (logger.isTraceEnabled()) {
        logger.trace("Executing " + invocation);
    }
    try {
        return getRemoteInvocationExecutor().invoke(invocation, targetObject);
    }
    catch (NoSuchMethodException ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Could not find target method for " + invocation, ex);
        }
        throw ex;
    }
    catch (IllegalAccessException ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Could not access target method for " + invocation, ex);
        }
        throw ex;
    }
    catch (InvocationTargetException ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Target method failed for " + invocation, ex.getTargetException());
        }
        throw ex;
    }
}

getRemoteInvocationExecutor() 获取远程调用执行器:

public RemoteInvocationExecutor getRemoteInvocationExecutor() {
    return this.remoteInvocationExecutor;
}

image.png
远程调用执行器执行 invoke

public Object invoke(RemoteInvocation invocation, Object targetObject)
    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{

    Assert.notNull(invocation, "RemoteInvocation must not be null");
    Assert.notNull(targetObject, "Target object must not be null");
    return invocation.invoke(targetObject);
}

public Object invoke(Object targetObject)
    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    Method method = targetObject.getClass().getMethod(this.methodName, this.parameterTypes);
    return method.invoke(targetObject, this.arguments);
}

获取的Method为服务接口的代理对象:
代理类型为:org.springframework.aop.framework.JdkDynamicAopProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // Get the interception chain for this method.
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...
            MethodInvocation invocation =
                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            retVal = invocation.proceed();
        }

        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

获取该服务接口的拦截器链:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

image.png

创建一个方法调用

//根据代理类、目标对象、方法、参数、目标对象类型、拦截器链创建方法调用
MethodInvocation invocation =
    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();

image.png
image.png

public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

org.springframework.remoting.support.RemoteInvocationTraceInterceptor#invoke
远程调用拦截器执行 invoke

public Object invoke(MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    if (logger.isDebugEnabled()) {
        logger.debug("Incoming " + this.exporterNameClause + "remote call: " +
                     ClassUtils.getQualifiedMethodName(method));
    }
    try {
        Object retVal = invocation.proceed();
        if (logger.isDebugEnabled()) {
            logger.debug("Finished processing of " + this.exporterNameClause + "remote call: " +
                         ClassUtils.getQualifiedMethodName(method));
        }
        return retVal;
    }
    catch (Throwable ex) {
        if (ex instanceof RuntimeException || ex instanceof Error) {
            if (logger.isWarnEnabled()) {
                logger.warn("Processing of " + this.exporterNameClause + "remote call resulted in fatal exception: " +
                            ClassUtils.getQualifiedMethodName(method), ex);
            }
        }
        else {
            if (logger.isInfoEnabled()) {
                logger.info("Processing of " + this.exporterNameClause + "remote call resulted in exception: " +
                            ClassUtils.getQualifiedMethodName(method), ex);
            }
        }
        throw ex;
    }
}