Instrumentation:
classFileTransformer: 提供自定义字节码转换方法,transfer方法在类被加载 重定义的时候被调用
- JavaAgent
- 获取所有已经加载过的类
- 获取所有已经初始化的类(执行过clinit)
- 获取某个对象大小
- 将某个jar加入到bootstrap classpath作为高优先级被bootstrapClassLoader加载
- 将某个jar加入到classpath里供AppClasloard加载
- 设置某些native方法前缀,在查找native方法的时候做规则匹配
- 使用
- 自己写的agent打包成jar
- 启动命令 java -javaagent:D:\tool_software\myagent.jar=param=someparam -jar 项目jar
- javaagent 指定装备jar路径
- java.lang.instrument 装配API
- Instrumentation
-
提供装备Java代码的服务方法(修改字节码机制),启动Agent时会传入premain或者agentmain方法
premain 和 agentmain区别: 用命令行 -javaagent 在程序启动前处理的 premain 在程序启动后处理的 agentmain
- ClassFileTransformer
- 需要一个它的实现类,以进行自定义字节码转换
- Javaassist
- 处理Java字节码类库
- 允许运行时定义类
- 加载类时修改类
- 提供两种级别API 源码级别、字节码级别
- 源代码级别支持编写java源码一样修改类文件,javaassist会即时编译
- 读取和写入字节码
Javassist.CtClass 是类文件的抽象表示形式
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Rectangle");
cc.setSuperclass(pool.get("test.Point"));
cc.writeFile();
byte[] b = cc.toBytecode();
Class clazz = cc.toClass();
- 定义类
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
CtNewMethod
addMethod
makeInterface
abstractMethod
- 创建类
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("com.du.Hello");
CtMethod ctMethod = CtMethod.make("public void sayHello(){System.out.println(\"Hello Javaassist\");}",ctClass);
ctClass.addMethod(ctMethod);
Class c = ctClass.toClass();
ctClass.detach();
Object o = c.newInstance();
Method method = c.getMethod("sayHello");
method.invoke(o);
输出:
Hello Javaassist
- 修改类
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.getCtClass("com.du.cdty.TestClass");
CtMethod ctMethod = ctClass.getDeclaredMethod("compute");
ctMethod.insertAfter("System.out.println(\"compute after param \"+java.util.Arrays.toString($args)+\",return \"+ $_);");
ctClass.toClass();
ctClass.detach();
TestClass testClass = new TestClass();
testClass.compute(10);
class TestClass{
public int compute(int param) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return param + 1000;
}
}
输出:
compute after param [10],return 1010
- 拦截方法
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setSuperclass(TestClass.class);
proxyFactory.setFilter(new MethodFilter() {
public boolean isHandled(Method method) {
if ("compute".equals(method.getName()))
return true;
return false;
}
});
Class aClass = proxyFactory.createClass();
TestClass testClass = (TestClass) aClass.newInstance();
((Proxy)testClass).setHandler(new MethodHandler() {
public Object invoke(Object o, Method method, Method method1, Object[] objects) throws Throwable {
long start = System.currentTimeMillis();
try {
return method1.invoke(o,objects);
} catch (Exception e){
throw e;
} finally {
long end = System.currentTimeMillis() - start;
System.out.println("call mathod name: "+method.getName()
+" cat time "+ end);
}
}
});
System.out.println(testClass.compute(11));
输出:
call mathod name: compute cat time 3000
1011