dubbo spi扩展实现机制javasist

Posted HelloWorld搬运工

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo spi扩展实现机制javasist相关的知识,希望对你有一定的参考价值。

Dubbo为了实现基于spi思想的扩展特性,特别是能够灵活添加额外功能,要能够动态生成一个叫做控制或适配并实现扩展或策略选择功能的类。当然对应已知需求如Protocol, ProxyFactory他们的策略选择的适配类代码dubbo直接提供也无妨,但是dubbo作为一个高扩展性的框架,使得用户能够添加自己的需求,根据配置动态生成自己的适配类代码,这样就需要在运行的时候去编译加载这个适配类的代码。

动态编译实现的类图:



编译接口定义

@SPI("javassist")

public interface Compiler {

Class<?> compile(String code,ClassLoaderclassLoader);

}

  • SPI注解表示如果没有配置,dubbo默认选用javassist编译源代码

  • 接口方法compile第一个入参code,就是java的源代码

  • 接口方法compile第二个入参classLoader,按理是类加载器用来加载编译后的字节码,其实没用到,都是根据当前线程或者调用方的classLoader加载的

  • SPI机制,(在java.util.ServiceLoader里有比较详细的介绍)简单来说就是为某个接口寻找服务实现的机制,实现方式可参看spi约定。

从接口定义代码我们可以看到dubbo使用了Javasist实现了SPI,接下来我们看看Javasist是怎么实现SPI的。

JavaSist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassistjboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

使用javasist生成字节码示例如下:

public static void main(String[]args)throws Exception{

        ClassPool pool =ClassPool.getDefault(); 

        //创建Programmer

        CtClass cc=pool.makeClass("com.mining.producer"); 

        //定义code方法 

        CtMethod method =CtNewMethod.make("public void code(){}", cc); 

        //插入方法代码 

       method.insertBefore("System.out.println("I'm aProgrammer,Just Coding.....");"); 

        cc.addMethod(method); 

        //保存生成的字节码 

        cc.writeFile("d://temp");

   }

运行代码后生成字节码类:

dubbo spi扩展实现机制javasist

Dubbo中使用Javasist框架生成代理

JavassistProxyFactory:利用字节码技术来创建对象。

dubbo spi扩展实现机制javasist

看似跟jdk生成代理一样,其实这里的Proxy类不是jdk中自带那个生成代理对象的类是:com.alibaba.dubbo.common.bytecode.Proxy

这个dubbo自己写的Proxy类,利用要代理的接口利用javassist工具生成代理代码。

获取Invoker 对象

dubbo spi扩展实现机制javasist

根据传入的 proxy对象的类信息创建对它的包装对象Wrapper

返回Invoker对象实例,这个invoker对象invoke方法可以根据传入的invocation对象中包含的方法名,方法参数来调用proxy对象返回调用结果

com.alibaba.dubbo.common.bytecode.Proxy生成代理对象的工具类

1.遍历所有入参接口,以;分割连接起来, 以它为keymap为缓存查找如果有,说明代理对象已创建返回

2.利用AtomicLong对象自增获取一个long数组来作为生产类的后缀,防止冲突

3.遍历接口获取所有定义的方法,加入到一个集合Set<String>worked,用来判重,

    获取方法y应该在methods数组中的索引下标ix

    获取方法的参数类型以及返回类型

    构建方法体return  ret= handler.invoke(this, methods[ix], args);

    这里的方法调用其实是委托给InvokerInvocationHandler实例对象的,去调用真正的实例方法加入到methods数组中

4.创建代理实例对象ProxyInstance

    类名为  pkg +“.poxy”+id = 包名 + “.poxy” +自增数值

    添加静态字段Method[]methods;

    添加实例对象InvokerInvocationHandlerhanler

    添加构造器参数是InvokerInvocationHandler

    添加无参构造器

    利用工具类ClassGenerator生成对应的字节码

5. 创建代理对象,它的newInstance(handler)方法用来创建基于我们接口的代理

dubbo spi扩展实现机制javasist

代理对象名Proxy + id

继承于Proxy, 所以要实现newInstance方法

添加默认构造器

实现方法newInstance代码, new pcn(hadler) 这里pcn就是前面生成的代理对象类名

利用工具类ClassGenerator生成字节码并实例化对象返回


以上是关于dubbo spi扩展实现机制javasist的主要内容,如果未能解决你的问题,请参考以下文章

Dubbo-扩展机制-SPI

dubbo学习-SPI机制

Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI

Dubbo-技术专区-扩展点加载机制Dubbo SPI

Dubbo-技术专区-扩展点加载机制Dubbo SPI

Dubbo的SPI机制