Javassist简介

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。它可以用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

1package com.javassis.test; 2 3public class CoolGuy { 4 private String hotGirlfriendName; 5 private String niceCarModel; 6 public String getHotGirlfriendName() { 7 return hotGirlfriendName; 8 } 9 public void setHotGirlfriendName(String hotGirlfriendName) { 10 this.hotGirlfriendName = hotGirlfriendName; 11 } 12 public String getNiceCarModel() { 13 return niceCarModel; 14 } 15 public void setNiceCarModel(String niceCarModel) { 16 this.niceCarModel = niceCarModel; 17 } 18 19} 20 21public class Person { 22 private int age; 23 24 public int getAge() { 25 return age; 26 } 27 28 public void setAge(int age) { 29 this.age = age; 30 } 31 32} 33 34package com.javassis.test; 35 36import java.io.IOException; 37import java.lang.reflect.InvocationTargetException; 38import java.lang.reflect.Method; 39 40import javassist.CannotCompileException; 41import javassist.ClassPool; 42import javassist.CtClass; 43import javassist.CtField; 44import javassist.CtMethod; 45import javassist.CtNewMethod; 46import javassist.Loader; 47import javassist.NotFoundException; 48 49public class Main { 50 public static void main(String[] args) throws NotFoundException, NoSuchMethodException, SecurityException, 51 InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, 52 CannotCompileException, IOException, ClassNotFoundException { 53 ClassPool classPool = ClassPool.getDefault(); 54 /** 55 * CoolGuy是已经存在的类 56 */ 57 CtClass ctc = classPool.get("com.javassis.test.CoolGuy"); 58 Class<?> c = ctc.toClass(); 59 Object cg = (Object) c.newInstance(); 60 Method m = c.getDeclaredMethod("setHotGirlfriendName", String.class); 61 m.invoke(cg, "柳岩"); 62 System.out.println("CoolGuy's girlfriend is " + ((CoolGuy) cg).getHotGirlfriendName()); 63 /** 64 * 动态构造一个SuckGuy类 65 */ 66 CtClass suckCt = classPool.makeClass("com.javassis.test.SuckGuy"); 67 // 添加域 68 CtField girlfriendField = new CtField(classPool.get("java.lang.String"), "hotGirlfriendName", suckCt); 69 CtField carField = new CtField(classPool.get("java.lang.String"), "niceCarModel", suckCt); 70 suckCt.addField(girlfriendField); 71 suckCt.addField(carField); 72 // 添加方法 73 CtMethod getMethod = CtNewMethod.make("public String getHotGirlfriendName() { return this.hotGirlfriendName;}", 74 suckCt); 75 CtMethod setMethod = CtNewMethod 76 .make("public void setHotGirlfriendName(String girl) { this.hotGirlfriendName = girl;}", suckCt); 77 suckCt.addMethod(getMethod); 78 suckCt.addMethod(setMethod); 79 getMethod = CtNewMethod.make("public String getNiceCarModel() { return this.niceCarModel;}", suckCt); 80 setMethod = CtNewMethod 81 .make("public void setNiceCarModel(String niceCarModel) { this.niceCarModel = niceCarModel;}", suckCt); 82 suckCt.addMethod(getMethod); 83 suckCt.addMethod(setMethod); 84 Class<?> cuckClass = suckCt.toClass(); 85 Object suckGuy = cuckClass.newInstance(); 86 m = cuckClass.getDeclaredMethod("setHotGirlfriendName", String.class); 87 m.invoke(suckGuy, "凤姐"); 88 String suckGirlfriend = (String) cuckClass.getMethod("getHotGirlfriendName", null).invoke(suckGuy, null); 89 System.out.println("SuckGuy's girlfriend is " + suckGirlfriend); 90 /** 91 * 设置CoolGuy父类为Person 92 */ 93 //ctc.writeFile(); 94 ctc.defrost(); // 先解冻,不然会报class is frozen异常, 95 // CtClass对象通过writeFile()、toClass()、toBytecode()转化为Class后,Javassist冻结了CtClass对象,因此,JVM不允许再次加载Class文件,所以不允许对其修改 96 ctc.setSuperclass(classPool.get("com.javassis.test.Person")); 97 Loader cl = new Loader(classPool);// 同一个classloader下不能加载两次class,所以新new一个loader 98 // c = ctc.toClass(); 99 c = cl.loadClass("com.javassis.test.CoolGuy"); 100 cg = (Object) c.newInstance(); 101 m = c.getMethod("setAge", int.class); 102 m.invoke(cg, 25); 103 m = c.getMethod("getAge", null); 104 System.out.println("CoolGuy's age is " + m.invoke(cg, null)); 105 } 106}

 输出:

CoolGuy's girlfriend is 柳岩
SuckGuy's girlfriend is 凤姐
CoolGuy's age is 25

几个需要注意的地方:

1、笔者用的Javassist版本是3.22.0-GA,比较坑爹的是它必须使用java 9,笔者一开始用java 8会报java.lang.NoClassDefFoundError: java/lang/StackWalker$Option

2、CtClass对象通过writeFile()、toClass()、toBytecode()转化为Class后,Javassist冻结了CtClass对象,因此,JVM不允许再次加载Class文件,所以不允许对其修改

,再次对其修改时需要defrost() 先解冻,不然会报class is frozen异常。

3、解冻后对类进行修改,之后使用修改后的类需要再次加载,此时虽然已经经过defrost() ,但同一个classloader下不能加载两次class,再次使用toClass()会报错,所以新new一个loader,使用loader.loadClass()完成Class加载。

接下来看看对如何动态的去修改类的方法:

思路就是把需要修改的方法改个新名字,然后新建一个新方法,名字命名为原名字,新方法copy原方法的内容并加入新的代码。

1package com.javassis.test; 2 3import javassist.ClassPool; 4import javassist.CtClass; 5import javassist.CtMethod; 6import javassist.CtNewMethod; 7 8/* 9 * 动态修改CoolGuy的一个方法 10 * */ 11public class UpdateTest { 12 public static void main(String[] args) throws Exception { 13 ClassPool classPool = ClassPool.getDefault(); 14 CtClass ctc = classPool.get("com.javassis.test.CoolGuy"); 15 String mName = "getHotGirlfriendName"; 16 CtMethod m = ctc.getDeclaredMethod(mName); 17 m.setName(mName + "$old"); 18 CtMethod nm = CtNewMethod.copy(m, mName, ctc, null); 19 StringBuffer mbody = new StringBuffer(); 20 mbody.append("{"); 21 mbody.append("\n return hotGirlfriendName"); 22 mbody.append(" + \"和鞠婧祎\"; \n"); 23 mbody.append("}"); 24 nm.setBody(mbody.toString()); 25 ctc.addMethod(nm); 26 Class<?> c = ctc.toClass(); 27 CoolGuy coolGuy = (CoolGuy)c.newInstance(); 28 coolGuy.setHotGirlfriendName("柳岩"); 29 System.out.println("CoolGuy's girlfriend is " + coolGuy.getHotGirlfriendName()); 30 } 31}

运行结果:

CoolGuy's girlfriend is 柳岩和鞠婧祎

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java基础知识随身记

2018年11月12日20:51:35一、基础知识:1、JVM、JRE和JDK的区别:JVM(JavaVirtualMachine):java虚拟机,用于保证java的跨平台的特性。  java语言是跨平台,jvm不是跨平台的。JRE(JavaRuntimeEnvironment):java的运行环境,包括jvmjava的核心类

Spring的两种代理JDK和CGLIB的区别浅谈

一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以

Android So动态加载 优雅实现与原理分析

背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我