1. ¿Qué es el Agente Java?
El Agente Java proporciona una forma de modificar el código de bytes cuando se carga. Hay dos formas de ejecutar: una es implementar a través de premain antes de que se ejecute el método principal; la otra es implementar a través de adjuntar api mientras el programa se está ejecutando
1) 、 Instrumentación
La instrumentación es una API proporcionada por JDK1.5 para interceptar eventos de carga de clases y modificar el código de bytes. Sus métodos principales son los siguientes:
public interface Instrumentation {
//注册一个转换器,类加载事件会被注册的转换器所拦截
void
addTransformer(ClassFileTransformer transformer, boolean canRetransform);
//重新触发类加载
void
retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
//直接替换类的定义
void
redefineClasses(ClassDefinition... definitions)
throws ClassNotFoundException, UnmodifiableClassException;
2), premain()
método
premain()
El método es el main()
método que se ejecuta antes que el método. Cuando se ejecuta, debe empaquetar el programa del agente en un paquete jar y agregar un comando para ejecutarlo al inicio:
-javaagent:<jarpath>[=<options>]
premain()
Se proporcionan los dos métodos de sobrecarga siguientes. Cuando se inicia la JVM, se intentará primero el primer método y, si no, se utilizará el segundo.
public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)
2. Ejecución que requiere mucho tiempo mediante el método de supervisión Byte Buddy
Byte Buddy es una biblioteca de generación y manipulación de código que se utiliza para crear y modificar clases de Java cuando se ejecuta una aplicación Java sin la ayuda de un compilador. Además de las utilidades de generación de código incluidas con la biblioteca de clases de Java, Byte Buddy también permite la creación de clases arbitrarias y no se limita a implementar interfaces para crear proxies en tiempo de ejecución. Además, Byte Buddy proporciona una API conveniente que puede usar un proxy Java o cambiar manualmente la clase durante el proceso de compilación.
Introduzca las dependencias relacionadas con Byte Buddy en pom.xml y especifique la ruta del archivo MANIFEST.MF
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ppdai</groupId>
<artifactId>bytebuddy-agent-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.8.20</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.8.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<!--指定MANIFEST.MF文件路径-->
<manifestFile>
src/main/resources/META-INF/MANIFEST.MF
</manifestFile>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
MethodCostTime:
public class MethodCostTime {
@RuntimeType
public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
long start = System.currentTimeMillis();
try {
//原有函数执行
return callable.call();
} finally {
System.out.println(method + " 方法耗时: " + (System.currentTimeMillis() - start) + "ms");
}
}
}
MyAgent :
public class MyAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("MyAgent init...");
AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> builder
//拦截任意方法
.method(ElementMatchers.any())
//委托
.intercept(MethodDelegation.to(MethodCostTime.class));
AgentBuilder.Listener listener = new AgentBuilder.Listener() {
@Override
public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
}
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {
}
@Override
public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {
}
@Override
public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
}
};
new AgentBuilder
.Default()
//指定需要拦截的类
.type(ElementMatchers.nameStartsWith("com.ppdai"))
.transform(transformer)
.with(listener)
.installOn(inst);
}
}
src/main/resources
Agregue un META-INF/MANIFEST.MF
archivo en el directorio , el contenido es el siguiente:
Manifest-Version: 1.0
Premain-Class: com.ppdai.agent.MyAgent
Can-Redefine-Classes: true
Utilizar mvn clean package
embalaje
Categoría de prueba :
public class AgentTest {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(new Random().nextInt(500));
}
}
Debe especificar la ruta del paquete jar del agente al ejecutar la clase de prueba
-javaagent:<jarpath>[=<options>]
Resultado de la operación :
MyAgent init...
public static void com.ppdai.test.AgentTest.main(java.lang.String[]) throws java.lang.InterruptedException 方法耗时: 350ms
Referencia :
https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650130323&idx=3&sn=f22cf468dd7a85539c5ab40cee8bb9ef
https://bugstack.blog.csdn.net/article/details/100044939
https://blog.csdn.net/generalfu/article/details/106086475