Javassist:使用-javaagent方式实现修改方法内容(打包和非打包方式执行)

Posted 你是小KS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javassist:使用-javaagent方式实现修改方法内容(打包和非打包方式执行)相关的知识,希望对你有一定的参考价值。

1. 声明

当前内容主要为使用Javassit+(-javaagent)方式实现修改方法体的内容(不修改源码的情况下)

当前内容参考:Javassit官方文档,和部分反编译

主要内容:

  1. 使用javassit修改某个类的某个方法,实现执行
  2. 打成jar包方式执行

pom依赖

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

2. 主要demo

1.实体类

public class User {
	private Integer id;
	private String username;
	private Date birth;
	// 省略get\\set\\toString\\无参\\有参

	public String getSchoolName(Integer id) {
		System.out.println("当前id=" + id);
		return "A中学";
	}

}

目标为修改getSchoolName的执行内容

2.主要main函数

public class MyJavaAssigentTest {
	public static void main(String[] args) {
		User user = new User(1, "张三", new Date());
		String schoolName = user.getSchoolName(1);
		System.out.println("当前的schoolName=" + schoolName);
		
		System.out.println("");
	}
}

一个非常简单的获取

3.主要javaAssigent类

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

/**
 * 
 * @author hy
 * @createTime 2021-06-19 13:15:43
 * @description 定义自己的javaassigent来实现在加载的时候修改加载后的字节码
 *
 */
public class MyJavaAssigent {
	public static void premain(String args, Instrumentation instrumentation) {
		MyClassFileTransformer transformer = new MyClassFileTransformer();
		instrumentation.addTransformer(transformer);
	}

	public static class MyClassFileTransformer implements ClassFileTransformer {
		@Override
		public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
				ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
			//	开始处理当前User类的getSchoolName方法,让其显示并返回数据
			// System.out.println("已加载:" + className);
			String modifyClassName="com.hy.vmopt.User";
			String modifyClassMethod="getSchoolName";
			String loadClassName=modifyClassName.replace(".", "/");
			// 表示找到了这个类
			if(loadClassName.equals(className)) {
				// 开始使用当前的javassist修改字节码文件
				try {
					ClassPool pool = ClassPool.getDefault();
					CtClass cc = pool.get(modifyClassName);
					CtMethod declaredMethod = cc.getDeclaredMethod(modifyClassMethod);
					// $1表示的就是第一个参数id
					
					declaredMethod.setBody("{System.out.println(\\"hello insert\\");"
							+ "$1=Integer.valueOf(6666);"
							+ "System.out.println(\\"当前id=\\" + $1);" + 
							"return \\"A中学\\";}");
					cc.detach();
					return cc.toBytecode();
				} catch (NotFoundException | CannotCompileException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return classfileBuffer;
		}
	}
}

这里的transform的className是xxx/xxx/xxx的形式,而javassist是xxx.xxx.xxx的形式

这里需要注意的是:

  1. $1表示第一个参数,其他的以此类推,注意$0就是this
  2. 使用赋值语句的时候需要使用Integer.valueOf方式转化(这个部分需要参考反编译jd-gui)
  3. 使用setBody时一定要使用{},否则加载字节码时报错

3. 开始非打包方式执行(javaagent需要打包)

1.具体的javaagent打包参考:博文

2.具体配置vm参数参考:博文

3.开始执行:
在这里插入图片描述

这个可以执行(原因:当前的pom依赖中具有javassist的库,所以可以执行)

4. 将两个包一起执行(命令行执行)

1.将具有javaagent的类的打包(并具有lib库),参考:博文

2.将具有main方法的类打包(空包),参考:博文

打包和修改后如下
在这里插入图片描述
其中myAssigent.jar中具有lib库的依赖,myUserTest.jar是main方法的jar

最后执行:

java -javaagent:myAssigent.jar -jar myUserTest.jar

直接执行成功!
在这里插入图片描述

以上是关于Javassist:使用-javaagent方式实现修改方法内容(打包和非打包方式执行)的主要内容,如果未能解决你的问题,请参考以下文章

Java Agent实例:方法监控

Java Agent实例:方法监控

Java Agent实例:方法监控

字节码JavaAgent的全链路监控篇二,通过字节码增加监控执行 耗时

javaAgent和Java字节码增强技术的学习与实践

字节码Javassist 通过字节码插桩监控方法采集运行时入参 出参和异常信息