javaagent
javaagent介绍
java.lang.Instrument
包是在JDK5引入的,开发者可以通过修改方法的字节码实现动态修改类代码。javaagent的主要功能如下:
可以在加载class文件之前做拦截,对字节码做修改(
premain
),可以实现AOP
、修改源代码等功能。可以在运行期对已加载类的字节码做变更(
agentmain
),可以实现热部署等功能。还有其他一些小众的功能
获取所有已经加载过的类
获取所有已经初始化过的类(执行过clinit方法,是上面的一个子集)
获取某个对象的大小
将某个jar加入到bootstrap classpath里作为高优先级被bootstrapClassloader加载
将某个jar加入到classpath里供AppClassloard去加载
设置某些native方法的前缀,主要在查找native方法的时候做规则匹配
premain用法示例
假如在项目agent-demo
中引入一个第三方jar包dog-1.0.0.jar
,其中有一个类com.maxwell.learning.javaagent.dog
,代码如下:
public class Dog{
public toString(){
return "i am a cute dog";
}
}
现在,想修改这个类的toString()
方法,修改为
public class Dog{
public toString(){
return "i am a cruel dog";
}
}
使用java agent
来修改Dog类的源码,需要写一个agent
和ClassFileTransformer
:
DogAgent
public class DogAgent {
private static final Logger logger = Logger.getLogger(DogAgent.class.getName());
private DogAgent() {
throw new InstantiationError("Must not instantiate this class");
}
public static void premain(String agentArgs, Instrumentation inst) {
ClassFileTransformer transformer = new DogTransformer();
inst.addTransformer(transformer, true);
}
}
ClassFileTransformer
public class DogTransformer implements ClassFileTransformer {
private static final Logger logger = Logger.getLogger(DogTransformer.class.getName());
private static final String DOG_CLASS_NAME="com.maxwell.learning.javaagent.dog.Dog";
@Override
public byte[] transform(ClassLoader loader, String classFile, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classFileBuffer) {
try {
final String className = toClassName(classFile);
if (DOG_CLASS_NAME.equals(className)) {
logger.info("Transforming class " + className);
CtClass clazz = getCtClass(classFileBuffer, loader);
//get doString() method
CtMethod method = clazz.getDeclaredMethod("toString");
//modify toString() method
method.setBody("return \"i am a cruel dog\";");
return clazz.toBytecode();
}
} catch (Throwable t) {
logger.log(Level.SEVERE, "Transforming class error", t);
}
return null;
}
private static String toClassName(String classFile) {
return classFile.replace('/', '.');
}
private static CtClass getCtClass(byte[] classFileBuffer, ClassLoader classLoader) throws IOException {
ClassPool classPool = new ClassPool(true);
if (classLoader == null) {
classPool.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));
} else {
classPool.appendClassPath(new LoaderClassPath(classLoader));
}
CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classFileBuffer), false);
clazz.defrost();
return clazz;
}
}