文章目录
- 开篇
- Javassist 常用类
- Javassist 的使用
-
- 如何实现类似 AOP 的功能
开篇
说起 AOP 小伙伴们肯定很熟悉,无论是 JDK 动态代理或者是 CGLIB 等,其底层都是通过操作 Java 字节码来实现代理。常用的一些操作字节码的技术有 ASM、AspectJ、Javassist 等。
ASM 其设计和实现是尽可能小而且快,更专注于性能。它在指令的层面来操作,所以使用它需要对 JVM 的指令有所了解,门槛较高,CGLIB 就使用了 ASM 技术。
AspectJ 扩展了 Java 语言,定义了一系列 AOP 语法,在 JVM 中运行需要使用特定的编译器生成遵守 Java 字节码规范的 Class 文件,Spring AOP 使用了 AspectJ 。
Javassist 直接使用 Java 编码的形式操作字节码,简单易上手,性能高于反射,相比于 ASM 稍低。
Javassist 常用类
Javassist 抽象出一个 ClassPool 对象来操作 Java 类,可以通过 ClassPool.getDefault() 来获取默认的 ClassPool 。常用的对象:
CtClass:代表一个 Class 的实例,可以通过类的全限定名来获取 CtClass 对象,其中包含了对 Class 的各种操作。
ClassPool:通过 HashTable 保存了路径下的 CtClass 信息,key为类的全限定名称,value 为类名对应的 CtClass 对象。
CtMethod、CtField:抽象出类的方法和属性,可以用于定义或修改方法和字段。
Javassist 的使用
依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
代码示例
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("com.aysaml.demo.javassist.User");
CtField field = new CtField(classPool.get("java.lang.String"), "name", ctClass);
field.setModifiers(Modifier.PRIVATE);
ctClass.addField(field);
ctClass.addMethod(CtNewMethod.getter("getName", field));
ctClass.addMethod(CtNewMethod.setter("setName", field));
CtConstructor noArgsCons = new CtConstructor(new CtClass[] {}, ctClass);
noArgsCons.setBody("{$0.name=\"mark\";}");
ctClass.addConstructor(noArgsCons);
CtConstructor hasArgsCons =
new CtConstructor(new CtClass[] {classPool.get("java.lang.String")}, ctClass);
hasArgsCons.setBody("{$0.name=$1;}");
ctClass.addConstructor(hasArgsCons);
CtMethod method = new CtMethod(CtClass.voidType, "printName", new CtClass[] {}, ctClass);
method.setBody("{System.out.println($0.name);}");
ctClass.addMethod(method);
ctClass.writeFile();
Object person = ctClass.toClass().newInstance();
如何实现类似 AOP 的功能
javassist 对于编程化的操作字节码是很简单易懂的,我们以在方法的开头结尾打印信息为例:
public class Cat {
private int num;
public void miao() {
this.num++;
}
}
我们要在 miao( ) 方法的前增加声音输出:
public static void main(String[] args) throws NotFoundException, CannotCompileException {
ClassPool classPool = ClassPool.getDefault();
CtClass catClass = classPool.get("com.aysaml.demo.javassist.Cat");
CtMethod method = catClass.getDeclaredMethod("miao");
method.insertBefore("System.out.println(\"miao~\");");
catClass.toClass();
Cat cat = new Cat();
cat.miao();
}
注意到,在使用 catClass.toClass() 加载被修改过的类时,强调必须保证在调用前这个类没有被加载过,否则会报 attempted duplicate class definition for name 异常。
我们知道一个类是不能被一个类加载器加载两次的,所以为了解决这个问题,需要制定一个没有加载过该类的 Classloader,Javassist 提供了一个 ClassLoader ,如下:
public class Cat {
private int num;
public void miao() {
System.out.println("调用了 miao 方法");
this.num++;
}
public static void main(String[] args) throws Exception{
ClassPool classPool = ClassPool.getDefault();
CtClass catClass = classPool.get("com.aysaml.demo.javassist.Cat");
CtMethod method = catClass.getDeclaredMethod("miao");
method.insertBefore("System.out.println(\"miao~\");");
Loader classLoader = new Loader(classPool);
Class clazz = classLoader.loadClass("com.aysaml.demo.javassist.Cat");
clazz.getDeclaredMethod("miao").invoke(clazz.newInstance());
}
}
执行结果为:
更多使用可以参考下面的链接:
http://www.javassist.org/tutorial/tutorial2.html
http://www.javassist.org/html/index.html
点赞 -收藏-关注-便于以后复习和收到最新内容
有其他问题在评论区讨论-或者私信我-收到会在第一时间回复
在本博客学习的技术不得以任何方式直接或者间接的从事违反中华人民共和国法律,内容仅供学习、交流与参考
免责声明:本文部分素材来源于网络,版权归原创者所有,如存在文章/图片/音视频等使用不当的情况,请随时私信联系我、以迅速采取适当措施,避免给双方造成不必要的经济损失。
感谢,配合,希望我的努力对你有帮助^_^
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)