RMI源码分析

Posted 歪歪梯Club

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RMI源码分析相关的知识,希望对你有一定的参考价值。


相关核心类

sun.rmi.server.UnicastServerRef
sun.rmi.server.UnicastRef
sun.rmi.server.Util
sun.rmi.transport.tcp.TCPEndpoint
sun.rmi.transport.LiveRef
java.rmi.Naming
sun.rmi.registry.RegistryImpl

rmi

RMI是Java的一组开发分布式应用程序的API。
RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。
简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用,由于J2EE是分布式程序平台,它以RMI机制实现程序组件在不同操作系统之间的通信。
比如,一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法。

简单使用rmi

要发布的服务接口

public interface HelloService extends Remote{
    public String sayHello() throws RemoteException;
    //要发布的服务类的方法必须都throws RemoteException
    //在Util中,创建代理对象时会checkMethod,存在没有throws RemoteException的则抛出IllegalArgumentException
}

util中的checkMethod

   public static Remote createProxy(Class<?> implClass,
                                     RemoteRef clientRef,
                                     boolean forceStubUse)

        throws StubNotFoundException
    
{
        .....
        final Class<?>[] interfaces = getRemoteInterfaces(implClass);
        .....
    }
    private static Class<?>[] getRemoteInterfaces(Class<?> remoteClass) {
        ArrayList<Class<?>> list = new ArrayList<>();
        getRemoteInterfaces(list, remoteClass);
        return list.toArray(new Class<?>[list.size()]);
    }

    private static void getRemoteInterfaces(ArrayList<Class<?>> list, Class<?> cl) {
        .....
        for (int i = 0; i < interfaces.length; i++) {
            Class<?> intf = interfaces[i];
            if (Remote.class.isAssignableFrom(intf)) {
                if (!(list.contains(intf))) {
                    Method[] methods = intf.getMethods();
                    for (int j = 0; j < methods.length; j++) {
                        checkMethod(methods[j]);
                    }
                    list.add(intf);
                }
            }
        }
    }

    private static void checkMethod(Method m) {
        Class<?>[] ex = m.getExceptionTypes();
        for (int i = 0; i < ex.length; i++) {
            if (ex[i].isAssignableFrom(RemoteException.class))
                return;
        }
        throw new IllegalArgumentException(
            "illegal remote method encountered: " + m);
    }

要发布的服务接口的实现类

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
    //需要继承UnicastRemoteObject,因为Remote只是接口,UnicastRemoteObject是Remote的实现类

    protected HelloServiceImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello() throws RemoteException{
        return "hello";
    }

}

服务发布端与客户消费端

public class Server {
    final static String host = "127.0.0.1";
    final static int port = 8080;
    public static void main(String[] args) throws RemoteException, MalformedURLException {
        HelloService helloService = new HelloServiceImpl();
        LocateRegistry.createRegistry(port);
        Naming.rebind("rmi://"+host+":"+port+"/hello", helloService);//不写port默认是1099
        System.out.println("服务启动...");
    }

}
public class Client {

    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        HelloService helloService = (HelloService) Naming.lookup("rmi://"+Server.host+":"+Server.port+"/hello");//默认端口1099
        System.out.println(helloService.sayHello());
    }

}

追踪源码

启动服务前,创建服务对象会调用父类无参构造
HelloServiceImpl

protected HelloServiceImpl() throws RemoteException {
        super();
    }

UnicastRemoteObject构造

    protected UnicastRemoteObject() throws RemoteException
    
{
        this(0);
    }
     protected UnicastRemoteObject(int port) throws RemoteException
    
{
        this.port = port;
        exportObject((Remote) this, port);
    }

在构造中调用了exportObject,将服务暴露出去

    public static Remote exportObject(Remote obj, int port)
        throws RemoteException
    
{
        return exportObject(obj, new UnicastServerRef(port));
    }
    private static Remote exportObject(Remote obj, UnicastServerRef sref)
        throws RemoteException
    
{
        if (obj instanceof UnicastRemoteObject) {
            ((UnicastRemoteObject) obj).ref = sref;
        }
        return sref.exportObject(obj, nullfalse);
    }

构造UnicastServerRef服务器对象来发布服务

    public UnicastServerRef(int port) {
        super(new LiveRef(port));
        this.filter = null;
    }

UnicastServerRef调用父类带参构造

    public UnicastRef(LiveRef liveRef{
        ref = liveRef;
    }

sref对象发布服务细节

    public Remote exportObject(Remote impl, Object data,
                               boolean permanent
)
        throws RemoteException
    
{
        Class<?> implClass = impl.getClass();
        Remote stub;

        try {
            stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
        } catch (IllegalArgumentException e) {
            throw new ExportException(
                "remote object implements illegal remote interface", e);
        }
        if (stub instanceof RemoteStub) {
            setSkeleton(impl);
        }

        Target target =
            new Target(impl, this, stub, ref.getObjID(), permanent);
        ref.exportObject(target);
        hashToMethod_Map = hashToMethod_Maps.get(implClass);
        return stub;
    }

创建代理对象,并生成一个可以真正发布的target,调用ref对象(上一步构建以后传递到父类构造函数的LiveRef)发布出去
LiveRef细节

    public LiveRef(int port) {
        this((new ObjID()), port);
    }
    public LiveRef(ObjID objID, int port) {
        this(objID, TCPEndpoint.getLocalEndpoint(port), true);
    }
    public LiveRef(ObjID objID, Endpoint endpoint, boolean isLocal) {
        ep = endpoint;
        id = objID;
        this.isLocal = isLocal;
    }
    public void exportObject(Target target) throws RemoteException {
        ep.exportObject(target);
    }

TCPEndpoint.getLocalEndpoint

    public static TCPEndpoint getLocalEndpoint(int port{
        return getLocalEndpoint(port, nullnull);
    }

    public static TCPEndpoint getLocalEndpoint(int port,
                                               RMIClientSocketFactory csf,
                                               RMIServerSocketFactory ssf
)
    
{
        /*
         * Find mapping for an endpoint key to the list of local unique
         * endpoints for this client/server socket factory pair (perhaps
         * null) for the specific port.
         */

        TCPEndpoint ep = null;

        synchronized (localEndpoints) {
            TCPEndpoint endpointKey = new TCPEndpoint(null, port, csf, ssf);
            LinkedList<TCPEndpoint> epList = localEndpoints.get(endpointKey);
            String localHost = resampleLocalHost();

            if (epList == null) {
                /*
                 * Create new endpoint list.
                 */

                ep = new TCPEndpoint(localHost, port, csf, ssf);
                epList = new LinkedList<TCPEndpoint>();
                epList.add(ep);
                ep.listenPort = port;
                ep.transport = new TCPTransport(epList);
                localEndpoints.put(endpointKey, epList);

                if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
                    TCPTransport.tcpLog.log(Log.BRIEF,
                        "created local endpoint for socket factory " + ssf +
                        " on port " + port);
                }
            } else {
                synchronized (epList) {
                    ep = epList.getLast();
                    String lastHost = ep.host;
                    int lastPort =  ep.port;
                    TCPTransport lastTransport = ep.transport;
                    // assert (localHost == null ^ lastHost != null)
                    if (localHost != null && !localHost.equals(lastHost)) {
                        /*
                         * Hostname has been updated; add updated endpoint
                         * to list.
                         */

                        if (lastPort != 0) {
                            /*
                             * Remove outdated endpoints only if the
                             * port has already been set on those endpoints.
                             */

                            epList.clear();
                        }
                        ep = new TCPEndpoint(localHost, lastPort, csf, ssf);
                        ep.listenPort = port;
                        ep.transport = lastTransport;
                        epList.add(ep);
                    }
                }
            }
        }

        return ep;
    }

最终获得一个可以发布服务的TCPEndpoint对象,并调用该对象把服务暴露出去

服务注册与拉取源码分析

注册服务
LocateRegistry.createRegistry(port);
创建注册中心时,会创建一个RegistryImpl对象

    public static Registry createRegistry(int port) throws RemoteException {
        return new RegistryImpl(port);
    }

RegistryImpl代表注册中心
RegistryImpl中使用一个HashTable来注册(bind)服务和查找(lookup)服务

    private Hashtable<String, Remote> bindings = new Hashtable<>(101);
    public RegistryImpl(int port)
        throws RemoteException
    
{
        if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                    public Void run() throws RemoteException {
                        LiveRef lref = new LiveRef(id, port);
                        setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
                        return null;
                    }
                }, nullnew SocketPermission("localhost:"+port, "listen,accept"));
            } catch (PrivilegedActionException pae) {
                throw (RemoteException)pae.getException();
            }
        } else {
            LiveRef lref = new LiveRef(id, port);
            setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
        }
    }
    private void setup(UnicastServerRef uref)
        throws RemoteException
    
{
        ref = uref;
        uref.exportObject(thisnulltrue);
    }
    public Remote lookup(String name)
        throws RemoteException, NotBoundException
    
{
        synchronized (bindings) {
            Remote obj = bindings.get(name);
            if (obj == null)
                throw new NotBoundException(name);
            return obj;
        }
    }
    public void bind(String name, Remote obj)
        throws RemoteException, AlreadyBoundException, AccessException
    
{
        synchronized (bindings) {
            Remote curr = bindings.get(name);
            if (curr != null)
                throw new AlreadyBoundException(name);
            bindings.put(name, obj);
        }
    }
    public void unbind(String name)
        throws RemoteException, NotBoundException, AccessException
    
{
        synchronized (bindings) {
            Remote obj = bindings.get(name);
            if (obj == null)
                throw new NotBoundException(name);
            bindings.remove(name);
        }
    }
    public void rebind(String name, Remote obj)
        throws RemoteException, AccessException
    
{
        bindings.put(name, obj);
    }
    .....

最终和服务端暴露服务类似,会把RegistryImpl对象暴露出去

当服务提供方调用Naming.rebind("rmi://"+host+":"+port+"/hello", helloService);注册服务时
会先根据端口获取到暴露的注册中心对象RegistryImpl,然后调用其方法注册
对应的客户端获取服务的过程也是类似(HelloService) Naming.lookup("rmi://"+Server.host+":"+Server.port+"/hello");
会先根据端口获取到暴露的注册中心对象RegistryImpl,然后调用其方法获取

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

        if (obj == null)
            throw new NullPointerException("cannot bind to null");

        registry.rebind(parsed.name, obj);
    }
    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);
    }
更多资料,请搜索公众号歪歪梯Club

以上是关于RMI源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

Java RMI地址解析问题

源码分析spring-mvc启动流程

《Docker 源码分析》全球首发啦!