Javassist (字节码修改工具)用法

Javassist简介

javassist是一个开源的分析、编辑和创建Java字节码的类库。可以对.class进行直接修改和创建,直接使用java编程的方式,不需要了解虚拟机的指令,就能动态的改变类的结构或者动态生成

Javassist 应用场景

应用性能的监控、动态代理等

重要类简介:

  • ClassPool:javassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似。

     使用方法: 

ClassPool pool = new ClassPool();
pool.appendSystemPath();

或者

ClassPool classPool = new ClassPool();
classPool.insertClassPath(new LoaderClassPath(loader));
  • CtClass: CtClass提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。不过,Javassist 并未提供删除类中字段、方法或者构造函数的任何方法。

           创建新类: pool.makeClass("com.ty.pojo.User");;

           加载已有类:pool.get("com.ty.javaagent.UserServiceImpl")

  • CtField:用来访问域
  • CtMethod :用来访问方法
  • CtConstructor:用来访问构造器
  • 生成并装载Class toClass()或者 toBytecode()

API地址:http://jboss-javassist.github.io/javassist/html/javassist/package-summary.html

语法:

符号  
$0,$1,$2.... $0表示this,其他的表示实际的参数
$args 参数数组,相当于new Object[]{$1,$2,.....}
$$ 所有的参数
$r 用于类型转换表示返回值的类型
$w 将基础类型转换为一个包装类型,如Integer a = ($w)5; 表示5转换为 Integer。 如果不是基本类型则什么都不做

a) 不能引用 在方法中其他地方定义的局部变量

b) 不会对类型进行强制检查

c) 使用特殊的项目语法符号


示例

导入javassist jar

      
            org.javassist
            javassist
            3.18.1-GA
            compile
        

1.创建新类

	@Test
	// 创建User类
	public void createUser() throws Exception {
		ClassPool pool = new ClassPool();
		pool.appendSystemPath();
		// 定义类
		CtClass userClass = pool.makeClass("com.ty.pojo.User");
		// id属性
		CtField idField = new CtField(CtClass.longType, "id", userClass);
		userClass.addField(idField);

		// name 属性
		CtField nameField = new CtField(pool.get("java.lang.String"), "name", userClass);
		userClass.addField(nameField);

		CtMethod getMethod = CtNewMethod.make("public String getName(){return this.name;}", userClass);
		CtMethod setMethod = CtNewMethod.make("public void setName(String name){this.name = name;}", userClass);

		userClass.addMethod(setMethod);
		userClass.addMethod(getMethod);

		Class clazz = userClass.toClass();

		System.err.println("class:" + clazz.getName());
		System.err.println("属性列表----------");
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(field.getType() + ":" + field.getName());
		}

		System.err.println("方法列表------------------------");
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println(method.getReturnType() + "-" + method.getName() + "-"
					+ Arrays.toString(method.getParameterTypes()));
		}

	}

结果:

属性列表----------
long  id
class java.lang.String  name
------------------------------------------------
方法列表------------------------
class java.lang.String   getName  []
void   setName  [class java.lang.String]

2.修改方法,统计方法的执行时间

@Test
	public void updateGetUserInfoMethod() throws Exception {
		ClassPool pool = new ClassPool();
		pool.appendSystemPath();
		// 定义类
		CtClass userServiceClass = pool.get("com.ty.javaagent.UserServiceImpl");
		// 需要修改的方法
		CtMethod method = userServiceClass.getDeclaredMethod("getUserInfo");
		// 修改原有的方法
		method.setName("getUserInfo$agent");
		// 创建新的方法,复制原来的方法
		CtMethod newMethod = CtNewMethod.copy(method, "getUserInfo", userServiceClass, null);
		// 注入的代码
		StringBuffer body = new StringBuffer();

		body.append("{\nlong start = System.currentTimeMillis();\n");
		// 调用原有代码,类似于method();($$)表示所有的参数
		body.append("getUserInfo$agent($$);\n");
		body.append("System.out.println(\" take \" +\n (System.currentTimeMillis()-start) + " + "\" ms.\");\n");

		body.append("}");
		newMethod.setBody(body.toString());
		// 增加新方法
		userServiceClass.addMethod(newMethod);

		UserServiceImpl userServiceImpl = (UserServiceImpl) userServiceClass.toClass().newInstance();
		userServiceImpl.getUserInfo();
	}

结果:

user:1
 take 2000 ms.

你可能感兴趣的:(java)