dubbo spi扩展实现机制javassist

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wufaliang003/article/details/80486846

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的。

javassist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用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中使用Javasist框架生成代理

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

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

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

获取Invoker 对象

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

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

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

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

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)方法用来创建基于我们接口的代理

代理对象名Proxy + id

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

添加默认构造器

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

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

关注微信公众号和今日头条,精彩文章持续更新中。。。。。



展开阅读全文

没有更多推荐了,返回首页