alljoyn:基于java动态代理的RPC实现原理分析

alljoyn是由高通开源,allseen组织下,作为IOT的一个开源软件框架。

本文分析它的core部分的远程调用方法的实现过程。

以android core sdk的release版本中的simple程序为例子。

(eg alljoyn-14.06.00a-android-sdk-rel\alljoyn-android\core\alljoyn-14.06.00a-rel\java\samples\simple\client)

1. 下面是一个定义为alljoyn接口,并定义了一个远程调用方法Ping.(早期版本的alljyon的RPC实现,需要依赖这个接口定义文件,

这个存在的问题是client, 和service端需要保持接口定义一致,service接口变化后,client不得不跟着更新这个文件,目前alljoyn做了改进,

可以通过将接口定义的元数据信息xml格式文件运行时再发送给client, client可以动态解析出接口(利用busattachment的createinterfacesfromxml))

/*
 * The BusInterface annotation is used to tell the code that this interface is an AllJoyn interface.
 *
 * The ‘name‘ value is used to specify by which name this interface will be known.  If the name is
 * not given the fully qualified name of the Java interface is be used.  In most instances its best
 * to assign an interface name since it helps promote code reuse.
 */
@BusInterface(name = "org.alljoyn.bus.samples.simple.SimpleInterface")
public interface SimpleInterface {

    /*
     * The BusMethod annotation signifies that this function should be used as part of the AllJoyn
     * interface.  The runtime is smart enough to figure out what the input and output of the method
     * is based on the input/output arguments of the Ping method.
     *
     * All methods that use the BusMethod annotation can throw a BusException and should indicate
     * this fact.
     */
    @BusMethod
    String Ping(String inStr) throws BusException;
}

2. 在client.java,使用这个接口的方法发送RPC请求,并接收返回值(Ping方法表面上看没有任何实现,其实已经封装在alljoyn内部,下面会分析到)

                    / * To communicate with an AllJoyn object, we create a ProxyBusObject.
                     * A ProxyBusObject is composed of a name, path, sessionID and interfaces.
                     *
                     * This ProxyBusObject is located at the well-known SERVICE_NAME, under path
                     * "/SimpleService", uses sessionID of CONTACT_PORT, and implements the SimpleInterface.
                     */
                    mProxyObj =  mBus.getProxyBusObject(SERVICE_NAME,
                                                        "/SimpleService",
                                                        sessionId.value,
                                                        new Class<?>[] { SimpleInterface.class });
                    /* We make calls to the methods of the AllJoyn object through one of its interfaces. */
                    mSimpleInterface =  mProxyObj.getInterface(SimpleInterface.class);
                    。。。 。。。
                    if (mSimpleInterface != null) {
                        sendUiMessage(MESSAGE_PING, msg.obj);
                        String reply = mSimpleInterface.Ping((String) msg.obj);
                        sendUiMessage(MESSAGE_PING_REPLY, reply);
                    }

3.原理分析

在 mBus.getProxyBusObject函数内部(我们可以看到有传递了new Class<?>[] { SimpleInterface.class }),

会new ProxyBusObject对象, 调用下面的构造函数。

    /**
     * Construct a ProxyBusObject.
     *
     * @param busAttachment  The connection the remote object is on.
     * @param busName        Well-known or unique bus name of remote object.
     * @param objPath        Object path of remote object.
     * @param sessionId      The session ID corresponding to the connection to the object.
     * @param busInterfaces  A list of BusInterfaces that this proxy should respond to.
     * @param secure         the security mode for the remote object
     */
    protected ProxyBusObject(BusAttachment busAttachment, String busName, String objPath, int sessionId,
                             Class[] busInterfaces, boolean secure) {
        this.bus = busAttachment;
        this.busName = busName;
        this.objPath = objPath;
        this.flags = 0;
        create(busAttachment, busName, objPath, sessionId, secure);
        replyTimeoutMsecs = 25000;

        //busInterfaces来自mBus.getProxyBusObject调用,传入的new Class<?>[] { SimpleInterface.class }参数值
        //使用java的Proxy类方法,Handler实现了java InvocationHandler 接口,proxy就是代理对象,来控制SimpleInterface具体主题对象Ping方法访问
        proxy = Proxy.newProxyInstance(busInterfaces[0].getClassLoader(), busInterfaces, new Handler());
        try {
            busConnectionLost =
                getClass().getDeclaredMethod("busConnectionLost", String.class);
            busConnectionLost.setAccessible(true);
        } catch (NoSuchMethodException ex) {
            /* This will not happen */
        }
    }
    /**
     * Gets a proxy to an interface of this remote bus object.
     *
     * @param <T> any class implementation of a interface annotated with AllJoyn interface annotations
     * @param intf one of the interfaces supplied when the proxy bus object was
     *             created
     * @return the proxy implementing the interface
     * @see BusAttachment#getProxyBusObject(String, String, int, Class[])
     */
    public <T> T getInterface(Class<T> intf) {
        @SuppressWarnings(value = "unchecked")
        T p = (T) proxy; //proxy被转成SimpleInterface
        return p;
    }
new Handler()实现java InvocationHandler接口, invoke最终Ping方法的触发(不说调用,理由见methodcall注释)
     public Object invoke(Object proxy, Method method, Object[] args) throws BusException {
            /*
             * Some notes on performance.
             *
             * Reflection is very expensive.  So first pass at optimization is to cache the
             * reflection calls that lookup names and annotations the first time the method is
             * invoked.
             *
             * Using a Method as a HashMap key is expensive.  Using method.getName() as the key
             * is less expensive.  But method names may not be unique (they can be overloaded), so
             * need to fall back to Method.equals if more than one method with the same name exists.
             */
            /*
                java动态代理,利用了反射机制,开销较大,所以Handler定义了一个Invocation类,和invocationCache
                private Map<String, List<Invocation>> invocationCache;, 使用方法名作为key, 并且由于被代理的接口
                方法名由于重载可能重名,所以需要用List<Invocation>
            */
            Invocation invocation = null;
            String methodName = method.getName();
            List<Invocation> invocationList = invocationCache.get(methodName);
            if (invocationList != null) {
                if (invocationList.size() == 1) {
                    /* The fast path. */
                    invocation = invocationList.get(0);
                } else {
                    /* The slow path.  Two Java methods exist with the same name for this proxy. */
                    for (Invocation i : invocationList) {
                        if (method.equals(i.method)) {
                            invocation = i;
                            break;
                        }
                    }
                    if (invocation == null) {
                        invocation = new Invocation(method);
                        invocationList.add(invocation);
                    }
                }
            } else {
                /*
                 * The very slow path.  The first time a proxy method is invoked.
                 *
                 * Walk through all the methods looking for ones that match the invoked method name.
                 * This creates a list of all the cached invocation information that we‘ll use later
                 * on the next method call.
                 */
                /*
                    第一次invoke到该方法后,可以将其缓存到Invocation对象,看下面Invocation类具体实现,
                    会将接口名,方法名,输入,输出参数,返回值类型记录下来            若无缓存,每次都需要遍历interfaces的方法列表
                  */
                invocationList = new ArrayList<Invocation>();
                for (Class<?> i : proxy.getClass().getInterfaces()) {
                    for (Method m : i.getMethods()) {
                        if (methodName.equals(m.getName())) {
                            Invocation in = new Invocation(m);
                            invocationList.add(in);
                            if (method.equals(in.method)) {
                                invocation = in;
                            }
                        }
                    }
                }
                if (invocation == null) {
                    throw new BusException("No such method: " + method);
                }
                invocationCache.put(methodName, invocationList);
            }

            Object value = null;
            if (invocation.isMethod) {
                /*一般java程序执行invoke时,下面执行的会是method.invoke, 并传入被代理对象实例参数,
                  eg. Object object=method.invoke(instanceObject, args);
                  而alljoyn则不一样,因为执行rpc方法,其实是将传进来的参数值列表序列化,并发送,而
                  这一过程alljoyn已经封装了统一接口,因此这里invokehandler的实现不需要有一个具体的被代理对象传入,
                  而只需获取到args, 并调用methodCall进行序列化并发送,等待响应结果返回value。          所以看不到Ping方法的实现。
             */
                value = methodCall(bus,
                                   invocation.interfaceName,
                                   invocation.methodName,
                                   invocation.inputSig,
                                   invocation.genericReturnType,
                                   args,
                                   replyTimeoutMsecs,
                                   flags);
            } else {
                if (invocation.isGet) {
                    Variant v = getProperty(bus,
                                            invocation.interfaceName,
                                            invocation.methodName);
                    value = v.getObject(invocation.genericReturnType);
                } else {
                    setProperty(bus,
                                invocation.interfaceName,
                                invocation.methodName,
                                invocation.outSig,
                                args[0]);
                }
            }

            /*
             * The JNI layer can‘t perform complete type checking (at least not easily),
             * so this extra code is here.  The conditions below are taken from the
             * InvocationHandler documentation.
             */
            boolean doThrow = false;
            Class<?> returnType = invocation.returnType;
            if (value == null) {
                doThrow = returnType.isPrimitive() && !returnType.isAssignableFrom(Void.TYPE);
            } else if (returnType.isPrimitive()) {
                if ((returnType.isAssignableFrom(Byte.TYPE) && !(value instanceof Byte))
                    || (returnType.isAssignableFrom(Short.TYPE) && !(value instanceof Short))
                    || (returnType.isAssignableFrom(Integer.TYPE) &&  !(value instanceof Integer))
                    || (returnType.isAssignableFrom(Long.TYPE) && !(value instanceof Long))
                    || (returnType.isAssignableFrom(Double.TYPE) && !(value instanceof Double))
                    || (returnType.isAssignableFrom(Boolean.TYPE) && !(value instanceof Boolean))) {
                    doThrow = true;
                }
            } else if (!returnType.isAssignableFrom(value.getClass())) {
                doThrow = true;
            }
            if (doThrow) {
                throw new MarshalBusException("cannot marshal ‘" + invocation.outSig + "‘ into " + returnType);
            }
            return value;
        }
    }
private class Invocation {
            public Method method;

            public boolean isMethod;
            public boolean isGet;

            public String inputSig;
            public String outSig;

            public String interfaceName;
            public String methodName;

            public Type genericReturnType;
            public Class<?> returnType;

            public Invocation(Method method) throws BusException {
                this.method = method;
                if (method.getAnnotation(BusProperty.class) != null) {
                    this.isGet = method.getName().startsWith("get");
                    this.outSig = InterfaceDescription.getPropertySig(method);
                } else {
                    this.isMethod = true;
                    this.outSig = InterfaceDescription.getOutSig(method);
                    this.inputSig = InterfaceDescription.getInputSig(method);
                }
                this.interfaceName = InterfaceDescription.getName(method.getDeclaringClass());
                this.methodName = InterfaceDescription.getName(method);
                this.genericReturnType = method.getGenericReturnType();
                this.returnType = method.getReturnType();
            }
        };
时间: 2024-10-03 23:00:35

alljoyn:基于java动态代理的RPC实现原理分析的相关文章

Java基础:动态代理在RPC框架中应用

转载请注明出处:jiq?钦's technical Blog RPC,远端过程调用.就是调用远端机器上的方法. 原理其实很简单,就是客户端上运行的程序在调用对象方法时,底层将针对该方法的调用转换为TCP/HTTP请求,发送到远端服务器,远端服务器监听固定端口,收到这个TCP/HTTP请求后会解析出相关信息,包括客户端想要调用哪个类的哪个方法,参数是什么等,然后进行对应的调用,将调用结果再通过数据包发回即可. RPC中一般会有一些"契约"的概念,即客户端和服务端双方约定好的接口,表明服务

深入理解java动态代理的实现机制

今天将从以下5方面来系统的学习一下java动态代理的实现机制: 什么是代理 什么是静态代理 什么是动态代理 动态代理的实现机制 动态代理的使用场景 1,什么是代理 相信大家都有购买过火车票或者机票的经历,有的人在携程买,有的在飞猪,也有的在微信上买等等,这里的携程飞猪微信也好都是受铁路部的委托代理售卖火车票,这里的携程飞猪就是代理类,铁路部就是委托类,这就是代理 2,什么是静态代理 所谓的静态代理就是在代码运行之前,代理类就已经存在,通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生

Java动态代理、CGLIB动态代理

开篇 Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理.为啥要这样呢, 是因为使用代理有 2 个优势: 可以隐藏委托类的实现 可以实现客户与委托类之间的解耦, 在不修改委托类代码的情况下能够做一些额外的处理 我们举个很常见的例子: 工厂会生产很多的玩具, 但是我们买玩具都是到商店买的, 而不是到工厂去买的, 工厂怎么生产我们并不关心, 我们只知道到商店可以买到自己想要的玩具,并且,如果我们需要送人的话商店可以把这些玩具使用礼品盒包装.这个工厂就是

java动态代理的实现

动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程. 友情提示:本文略有难度,读者需具备代理模式相关基础知识,. 通常情况下,代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy).那么有没有一种机制能够让系统在运行时动态创建代理类?答案就是本文将要介绍的动态代理(Dynamic Proxy).动态代理是一种较

java 动态代理示例,带主要注释

Java proxy是基于反射,仅仅支持基于接口的动态代理. java 动态代理是一切架构的基础,必须了解. 废话少说,先上代码获得感性认识. 示例代码有主要注释. 接口: public interface Subject { String hello(String name); void say();} 接口实现: public class ImpSubject implements Subject { @Override public String hello(String name){ r

java动态代理框架

java动态代理是一个挺有意思的东西,他有时候可以被使用的很灵活.像rpc的调用,调用方只是定义的一个接口,动态代理让他匹配上对应的不同接口:mybatis内部的实现,编码时,只是实现了mapper层的接口和sql的xml的配置,动态代理把他们连起来.记得之前在一家公司,他们使用thrift做rpc的解决方案,每个项目都得管理thrift的连接和关闭,代码考来考去,在spring下还得不断new对象.后来参照mybatis的实现方式,使用动态代理,做成spring注入的方式,方便很多,程序员只需

Java 动态代理分析

Java的代理有两种:静态代理和动态代理,动态代理又分为 基于jdk的动态代理 和 基于cglib的动态代理 ,两者都是通过动态生成代理类的方法实现的,但是基于jdk的动态代理需要委托类实现接口,基于cglib的动态代理不要求委托类实现接口. 接下来主要分析一下基于jdk的动态代理的实现原理. 一 动态代理例子 首先来看一个动态代理的例子: # 测试类,主要功能是生成代理类并调用代理方法 TargetFactory.javapublic class TargetFactory { public

JAVA 动态代理学习记录

打算用JAVA实现一个简单的RPC框架,看完RPC参考代码之后,感觉RPC的实现主要用到了两个方面的JAVA知识:网络通信和动态代理.因此,先补补动态代理的知识.---多看看代码中写的注释 参考:Java 代理模式与动态代理类 java的动态代理机制详解 在动态代理中,首先定义一个接口,这个接口中声明的方法 是 真实类需要实现的,真实类实现该方法来提供具体的操作. public interface Subject { public abstract void request(); } publi

【转载】Java 动态代理

Java 动态代理 本文为 Android 开源项目源码解析 公共技术点中的 动态代理 部分项目地址:Jave Proxy,分析的版本:openjdk 1.6,Demo 地址:Proxy Demo分析者:Caij,校对者:Trinea,校对状态:完成 1. 相关概念 1.1 代理 在某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种方式我们就称为代理.这里对象 A 所属类我们称为委托类,也称为被代理类,对象 B 所属类称为代理类.代理