surfacecreated啥时被调用_javassist demo的理解 在dubbo中的使用 减少反射开销 启动时就生成wrapper...

学习于:掘金小册 《深度剖析apache dubbo 核心技术内幕》

web:Java动态字节技术之Javassist

看dubbo的时候重新看到了这东西,然后捡起一些东西来写写。

javassist是一个字节码修改工具,不需要了解字节码指令(同类型的ASM需要),性能比ASM稍差些,但是简单。操作起来跟java反射有些类似。

主要有 ClassPool ,CtClass,CtMethod,CtField,,构造器等。

顾名思义,基本可以理解是干啥的,classpool用来存储ctclass,工作方式与 JVM 类装载器非常相似。

概念啥的网上看看就差不多了,我这里也是基本层次的了解,写个demo比较好,写个复制一个类,然后修改它的方法体,然后加个属性,然后修改类名,然后调用方法的demo。

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.20.0-GA</version>
        </dependency>

这是maven依赖。

 public static void main(String[] args) throws Exception{
        ClassPool cp = ClassPool.getDefault();
        //从池中获取全限定名的ctclass
        CtClass ct = cp.get("com.hbr.service.MyWorld");
        String name = ct.getName();
        System.out.println(name);
        String packageName = ct.getPackageName();
        System.out.println(packageName);

        //复制原来的类结构,创建一个新的类
        ct.setName("com.hbr.service.$MyWorld");

        //增加foo方法
        CtMethod ctMethod = new CtMethod(CtClass.intType,"foo",new CtClass[]{CtClass.intType,CtClass.intType},ct);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("return $1*$2;");  //$1  $2 代替形参
        ct.addMethod(ctMethod); //加入新的方法

        //增加一个属性
        CtField ctField = new CtField(CtClass.intType,"age",ct);
        ctField.setModifiers(Modifier.PUBLIC);
        ct.addField(ctField,"18");

        //写入到idea的maven的target目录下,写在其它目录可能不会加载,idea和maven的配置问题
        ct.writeFile(ClassPool.class.getClassLoader().getResource(".").getFile());

        //ctclass到class的转换
        Class clazz = ct.toClass();
        Object instance = clazz.newInstance();
        Method fooMethod = clazz.getMethod("foo", int.class, int.class);//这里不能使用integer.class
        Object result = fooMethod.invoke(instance, 2, 2);
        System.out.println(result);
    }

ClassPool cp = ClassPool.getDefault();

Returns the default class pool. The returned object is always identical since this method is a singleton factory.

The default class pool searches the system search path, which usually includes the platform library, extension libraries, and the search path specified by the -classpath option or the CLASSPATH environment variable.

这里注意它的搜索路径

When this method is called for the first time, the default class pool is created with the following code snippet:

ClassPool cp = new ClassPool();

cp.appendSystemPath();

这是它的替代方式

If the default class pool cannot find any class files, try ClassClassPath and LoaderClassPath.

总之就是返回一个包含了默认路径的class的class池。这个理解比较重要。

然后可以通过

cp.insertClassPath("D:codemygitdubbostudyoutyame");

这个方法设定搜索类的路径。(说实话,这个方法有点坑,特别是在maven工程中,跟target目录之类的相关)

String path=ClassPool.class.getClassLoader().getResource(".").getFile();

这个可以获取target下的class路径

CtClass ctClass2 = cp.makeClass("com.hbr.service.MyWorld");

ctClass2.writeFile(path);

这样就可以创建一个包名为com.hbr.service,类名为MyWorld的class。

a49c9bf7984caff8153b55561fa7e612.png

这样就是单纯的没有.java文件,直接构造一个class文件,就这样

fbb8708a9f185105b018a1def2439331.png

然后回到我们的demo

输入

382c8082715e3452ddbd1f7e4d427714.png

输出:

abf51fc0d4fdd49db71241ed1f029cf9.png

4a5fd213b09b3ea12a68442fed24e2fb.png

结构是

16ea4a63d2ce1975f925e48d76063ec0.png

结果增加了一个foo方法,然后反射调用了这个foo方法,然后我把class写入到了classes目录下。(其实是可以不写的) 大家可以自己尝试着操作里面的api,加深一下印象。

然后回到dubbo这里。dubbo会给每个服务提供者的实现类生产一个wrapper类,它最终调用服务提供方的接口实现类,wrapper类的存在是为了减少反射的调用

可以查看 dubbo生成实现类的Wrapper类_Venture758的专栏-CSDN博客

重点:public class Wrapper1 extends Wrapper implements ClassGenerator.DC

86d823ab6fbebf1cd0c63578880ce309.png

反射传过来类名,方法名,参数类型和参数值。 通过类名我们可以反射获取实例,也就是上面的object,方法名直接是上面的string,参数之类的直接传递即可。

一般来说比如jdk的动态代理就是反射 method.invoke(instance,args)

而这里是invokeMethod直接调用你的这个方法,跟平常的调用没什么区别。 这里手动判断方法名,然后控制要调用的的方法,虽然生成这个类的操作没有反射操作这么简单,但是真正调用的时候,开销就比较小。 而且关键是,生成这个wrapper类的操作虽然比较麻烦,但是它是在dubbo服务启动时生成的,所以不会对运行时带来开销

然后看看这个wrapper类的生成。

7657a8ec063db2476db798db63e60029.png

默认有两种方式,一个是JavassistProxyFactory,另一个是JdkProxyFactory。spi默认使用javassist。

看一下源码实现:

2006b751e5ab282e9e56c3c196f94ca3.png

跟进去

9f82b05b28196aace226bf8cafcd63d1.png

进到ClassGenerator里面就是我们熟悉的javassist的api操作了。

然后反手看jdk那个

6dd58a8dcb415b785ab348fae7f95c44.png

直接反射调用。没啥好说的。

值得注意的是上面的getproxy中的proxy,jdk的proxy是反射包的,javassist的proxy是dubbo包的,即javasist动态生成Proxy再调用newinstance。

d019dc4d742bc0b01139770099e83d73.png

也就是完全替代了动态代理的切面增强功能。。。定制化非常固化,适合框架使用,我们还是使用jdk的动态代理比较好,或者cglib吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值