javassist:字节码编辑器工具
简介:
javassist是一款可以在运行时生成字节码的工具,可以通过它来构造一个新的class对象、method对象,这个class是运行时生成的。可以通过简短的几行代码就可以生成一个新的class type
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Rectangle");
cc.setSuperclass(pool.get("test.Point"));
//CtClass cc = pool.makeClass("Point");
cc.writeFile("path");
上面的代码就会把新生成的class 文件写到文件系统中,javassist封装了从 方法字符串 到 字节码的逻辑,用户可以方便的像写程序一样,生成一个新类。
运行时动态生成class,有点类似cglib,和cglib不同的是,javassist可以直接编辑类里面的属性、方法源码,而cglib没有封装这些接口。
举例说明,在实现一个aop功能时,javassist可以通过重新写method的源码来实现,而cglib需要实现类型MethodInvoker这种接口。
javassist可以修改method的源代码,执行这些代码不需要反射,就像执行提前编写好的硬代码一样,而cglib的callback是反射实现的。当然,他们的效率不会有太大差异,各种缓存策略也保证了他们的执行效率;
下面是一些基础知识
If a CtClass object is converted into a class file by writeFile(), toClass(), or toBytecode(), Javassist freezes that CtClass object. Further modifications
of that CtClass object are not permitted A frozenCtClass
can be defrost so that modifications of the class definition will be permitted
CtClasss cc = ...;
:
cc.writeFile();
cc.defrost();
cc.setSuperclass(...); // OK since the class is not frozen.
After defrost()
is called, the CtClass
object can be modified again.
如果想让CtClass不可修改,可以使用stopPruning
CtClasss cc = ...;
cc.stopPruning(true); //不能修剪
:
cc.writeFile(); // convert to a class file.
// cc is not pruned.
一般来说运行时再修改CtClass风险太大,不建议修改它,尤其是执行toClass方法后
Class clz = cc.toClass();
T instance = (T) clz.newInstance();
由于ClassPool.getDefault() 搜索class时用的classpath和当前的是JVM级的class path是相同的,同时它的classLoader是当前线程上下文的classloader,也就是App classloader,因此如果一个应用使用tomcat启动时,可能无法找到当前webapp对应的用户的classpath(一个tomcat占用一个jvm,而一个tomcat可以运行多个webapp,每个webapp都是不同的class loader、classpath),也就无法搜索到对应的class。
可以使用
pool.insertClassPath(new ClassClassPath(this.getClass()));
来插入classpath,这样,不同的webapp 搜索class 时,也就能找到自己的class,而不会交叉。
规避内存溢出
如果ClassPool中有非常多的CtClass
,有可能会导致内存溢出,因此提供了一个方法,可以把不用的CtClass
删掉
CtClass cc = ... ;
cc.writeFile();
cc.detach();
调用detach()方法后,就不能再操作CtClass对象了,但是可以通过 pool.get("test.Rectangle") 来重新加载该对象
或者重新创建一个ClassPool(按照文档的意思,是里面的CtClass你也丢弃了),没有引用链的老ClassPool就被垃圾回收了,包括里面的CtClass
ClassPool cp = new ClassPool(true);
// if needed, append an extra search path by appendClassPath()
//因为 new ClassPool(true)相当于ClassPool cp = new ClassPool();cp.appendSystemPath(); // or append another path by appendClassPath()
个人理解在tomcat启动的应用,构建ClassPool有两种方式
1:
ClassPool parent = ClassPool.getDefault();
ClassPool pool = new ClassPool(parent);
//tomcat下启动,不同的webapp有不同的classpath
pool.insertClassPath(new ClassClassPath(ProxyFactory.class));
2:
ClassPool pool = new ClassPool(false);
//tomcat下启动,不同的webapp有不同的classpath
pool.insertClassPath(new ClassClassPath(ProxyFactory.class));
区别就是第2中没有parent classloader,全部的CtClass都在自己的ClassPool对象中,而1中,有些CtClass可能被放到parent中
下面的ClassPool对象,通过get方法获取CtClass的源码
/**
* @param useCache false if the cached CtClass must be ignored.
* @return null if the class could not be found.
*/
protected synchronized CtClass get0(String classname, boolean useCache)
throws NotFoundException
{
CtClass clazz = null;
if (useCache) {
clazz = getCached(classname);
if (clazz != null)
return clazz;
} if (!childFirstLookup && parent != null) { //childFirstLookup默认值为false
clazz = parent.get0(classname, useCache);
if (clazz != null)
return clazz;
} clazz = createCtClass(classname, useCache);
if (clazz != null) {
// clazz.getName() != classname if classname is "[L<name>;".
if (useCache)
cacheCtClass(clazz.getName(), clazz, false); return clazz;
} if (childFirstLookup && parent != null)
clazz = parent.get0(classname, useCache); return clazz;
}
Classloader部分:待续
顺便一提tomcat中启动应用的classloader结构,下图中从下到上的关系中,上为parent。
Bootstrap classloader 是最底层的ClassLoader,它没有parent,加载的是java最核心的类和包,相传它是C++直接写的;
ExtClassLoader加载的是扩展包%JAVA_HOME%/jre/lib/ext目录下的一些包,它的parent是null,表示它是仅次于Bootstrap ClassLoader,也属于最底层的ClassLoader;
AppClassLoader就是我们运行一个普通的java 程序时,我们自己写的类会使用AppClassLoader来加载;
基于tomcat容器,tomcat会在AppClassLoader上创建子ClassLoader StandardClassLoader,而此时的AppClassLoader仅会加载tomcat的bootstrap.jar和juli.jar,StandardClassLoader则会加载更多的tomcat的lib包,作为一个基础的ClassLoader,这么做的用意是防止tomcat的lib包影响到tomcat的正常启动;
WebAppClassLoader是扔进tomcat中的引用代码(即我们自己的类)的类加载器,它的parent是StandardClassLoader;
同时,WebAppClassLoader加载的类,它的线程上下文ClassLoader也会被设置成WebAppClassLoader,如果一个tomcat中有两个应用,很显然,他们是两个类加载器,因此,javassist文档中的注意事项也是可以忽略的,原因是ClassPool.getDefault()方法会使用线程上下文ClassLoader,因此它会找到一个正确的ClassLoader,也就对应了一个正确的classpath
内省和定制
内省这里简单理解成调用属性域的get,set方法,在javassist中,通过java 反射api实现,比如你通过定制在CtClass中新添了一个属性,然后你可以调用set方法来给该属性赋值
下面来说说定制,javassist中,method对象的原型是CtMethod
CtMethod ctMethod = CtNewMethod.make(sb.toString(), cc);
上面的代码是增加一个新的方法,sb.toString()代表的是这个方法的字符串,如“public int geti(){return i;}”,它是一个完整的方法体
同时ctMethod有多个方法可以操作
获取CtMethod 对象后,还可以再操作这个方法,如insertBefore(),insertAfter()等等
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
CtMethod m = cc.getDeclaredMethod("move");
m.insertBefore("{ System.out.println($1); System.out.println($2); }");
cc.writeFile();
标识符,它们都以$开头,适用于编写方法,获取或操作方法的参数
$0, $1, $2, ... $0表示this, $n,...获取第n个参数
$args An array of parameters. The type of $args is Object[].
int 会被转换成Integer,再用$args[0]时,会转回int
$$ All actual parameters.
For example, m($$) is equivalent to m($1,$2,...) $cflow(...) cflow variable 返回递归调用成层数,0表示调用一次
$r The result type. It is used in a cast expression. 与$w相反
$w The wrapper type. It is used in a cast expression. ($w)$1可以把int转成Integer
$_ The resulting value
$sig An array of java.lang.Class objects representing the formal parameter types.
$type A java.lang.Class object representing the formal result type.
$class A java.lang.Class object representing the class currently edited.
在编码过程中,我遇到过int 直接放入Object[]时,VefifyTypeError,即Object[]中只能放封装类型,如Integer,而不能放原始类型int,而平常我们写代码试,把int放入Object[]时没有报错的原因是java编译器在编译成class文件时,已经将int做了转换。而目前的javassist还没有这么智能,但是预留了$w来处理这个问题。
关于各种标识符,可以参考官方文档:http://jboss-javassist.github.io/javassist/tutorial/tutorial2.html
使用javassist的代码:
https://github.com/jianliu/lsf/blob/master/src/main/java/com/liuj/lsf/client/ProxyFactory.java
javassist:字节码编辑器工具的更多相关文章
- Javassist 字节码 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Javassist字节码增强示例
概述 Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果.熟练使用这套工具,可以让Java编程更接近与动态语言编程. 下面一个方 ...
- Javassist 字节码 语法 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Atitit.java 虚拟机的构成 与指令分类 与 指令集合 以及字节码查看工具javjap
Atitit.java 虚拟机的构成 与指令分类 与 指令集合 以及字节码查看工具javjap 1.1. 虚拟机的构成 java虚拟机--处理器.堆栈.寄存器.指令系统. 1 1.2. 虚拟机执行过程 ...
- 5.Dubbo原理解析-代理之Javassist字节码技术生成代理 (转)
转载自 斩秋的专栏 http://blog.csdn.net/quhongwei_zhanqiu/article/details/41597219 JavassistProxyFactory:利用 ...
- Dubbo原理实现之使用Javassist字节码结束构建代理对象
JavassistProxyFactory利用自己吗技术构建代理对象的实现如下: public <T> T getProxy(Invoker<T> invoker, Class ...
- JAVAssist字节码操作
Java动态性的两种常见实现方式 字节码操作 反射 运行时操作字节码可以让我们实现如下功能: 动态生成新的类 动态改变某个类的结构(添加/删除/修改 新的属性/方法) 优势: 比反射开销小,性能高 ...
- Javassist 字节码操作
1.读写字节码 Javassist是用来处理java字节码的类库.字节码保存在二进制文件中称为类文件.每个类文件夹包括一个java类或接口. Javasssist.CtClass这个类是一个类文件的抽 ...
- 如何使用vscode-代码编辑器工具
vscode-代码编辑器的全称是“visual studio code”,主要是一个运行于 Mac OS X.Windows和 Linux 之上的,针对于编写现代 Web 和云应用的跨平台源代码编辑器 ...
随机推荐
- Java就业企业面试问题-ssh框架
SSH框架阶段SSH的优缺点,使用场景? Hibernate优点: (1) 对象/关系数据库映射(ORM) 它使用时只需要操纵对象,使开发更对象化,抛弃了数据库中心的思想,完全的面向对象思想 ...
- 如何获取系统Home(Launcher)应用判断用户是否处于home界面
要把我们的应用程序作为home(launcher应用),只需要在AndroidManifest.xml中添加: <category android:name="android.inte ...
- 27.Linux-DM9000C网卡移植(详解)
上一节 我们学习了: 网卡驱动介绍以及制作虚拟网卡驱动: http://www.cnblogs.com/lifexy/p/7763352.html 接下来本节,学习网卡芯片DM9000C,如何编写 ...
- Jimmychoo商城系统总结
一.需求 1.游戏模块 ①在进入H5之前,首先有一个动态的探照灯的动效,然后由"淡出"效果到H5首页. ②在点击"开始游戏"之后会有一段动画演示游戏内容,然后滑 ...
- AngularJS学习篇(一)
AngularJS 使用 表达式 把数据绑定到 HTML. AngularJS 表达式 AngularJS 表达式写在双大括号内:{{ expression }}. AngularJS 表达式把数据绑 ...
- word建立统一的表格样式
插入一个表格,一般border都是一样粗细,不美观, 这里推荐一种样式如下图(外框和首行都加粗,比较好看) 设置方法: 1.选中表格,上方出现设计选项卡 2.表格样式,点击"新建样式表&qu ...
- 诸葛马前课andoid app 应用
前段时间学了点安卓开发的知识,也在同时,陪家人看了<新闺蜜时代 >的后面几集,其中,周小北提到了诸葛马前课. 于是网上查了些资料,学习了一下马前课的计算方法,本着程序服务生活的原则,省去不 ...
- 一次触摸,Android到底干了啥
WeTest 导读 当我们在写带有UI的程序的时候,如果想获取输入事件,仅仅是写一个回调函数,比如(onKeyEvent,onTouchEvent-.),输入事件有可能来自按键的,来自触摸的,也有来自 ...
- python调用c代码
Linux环境下使用python调用C的printf例子: #!/usr/bin/env python2.7 #-*- coding:utf-8 -*- from ctypes import * de ...
- JavaScript学习笔记(十)——高阶函数之map,reduce,filter,sort
在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...