1. What is Java Agent
Java Agent provides a way to modify the bytecode when it is loaded. There are two ways to execute: one is to achieve through premain before the main method is executed; the other is to implement through attach api during program operation
1)、Instrumentation
Instrumentation is an API provided by JDK1.5 to intercept class loading events and modify bytecode. Its main methods are as follows:
public interface Instrumentation {
//注册一个转换器,类加载事件会被注册的转换器所拦截
void
addTransformer(ClassFileTransformer transformer, boolean canRetransform);
//重新触发类加载
void
retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
//直接替换类的定义
void
redefineClasses(ClassDefinition... definitions)
throws ClassNotFoundException, UnmodifiableClassException;
2), premain()
method
premain()
The method is the main()
method that runs before the method. When running, you need to package the agent program into a jar package and add a command to execute it at startup:
-javaagent:<jarpath>[=<options>]
premain()
The following two overloading methods are provided. When the JVM starts, the first method will be tried first, and the second method will be used if not
public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)
2. Time-consuming execution using Byte Buddy monitoring method
Byte Buddy is a code generation and manipulation library used to create and modify Java classes when a Java application is running without the help of a compiler. In addition to the code generation utilities included with the Java class library, Byte Buddy also allows the creation of arbitrary classes and is not limited to implementing interfaces for creating runtime proxies. In addition, Byte Buddy provides a convenient API that can use a Java proxy or manually change the class during the build process
Introduce Byte Buddy related dependencies in pom.xml and specify the path of the MANIFEST.MF file
<?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
Add a META-INF/MANIFEST.MF
file in the directory , the content is as follows:
Manifest-Version: 1.0
Premain-Class: com.ppdai.agent.MyAgent
Can-Redefine-Classes: true
Use mvn clean package
packaging
Test category :
public class AgentTest {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(new Random().nextInt(500));
}
}
You need to specify the agent's jar package path when running the test class
-javaagent:<jarpath>[=<options>]
Operation result :
MyAgent init...
public static void com.ppdai.test.AgentTest.main(java.lang.String[]) throws java.lang.InterruptedException 方法耗时: 350ms
The source code has been uploaded to GitHub : https://github.com/hxt970311/bytebuddy-agent-demo
3. Time-consuming execution using AspectJ LTW monitoring method
As a complete solution for AOP programming, AspectJ provides three weaving opportunities, namely:
- compile-time: Compile-time weaving, directly compile the .class file containing the weaving code at compile time
- post-compile: weaving after compilation, enhancing the compiled classes
- load-time: weaving when the JVM is loading classes
Introduce aspectj related dependencies :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
Writing Aspect :
@Aspect
public class MethodCostTimeAspect {
@Pointcut("execution(* com.ppdai..*(..))")
public void pointcut() {
}
@Around("pointcut()")
public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
System.out.println(joinPoint.getSignature() + " 方法耗时: " + (System.currentTimeMillis() - start) + "ms");
return result;
}
}
src/main/resources
Add a META-INF/aop.xml
file in the directory , specify the Aspect class and the class to be woven, the content is as follows:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<aspects>
<aspect name="com.ppdai.MethodCostTimeAspect"/>
<!--需要被织入的类-->
<weaver options="-verbose -showWeaveInfo">
<include within="com.ppdai..*"/>
</weaver>
</aspects>
</aspectj>
Test category :
public class AspectjLtwTest {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(new Random().nextInt(500));
}
}
Specify the jar package path of agent aspectjweaver when running the test class
-javaagent:/Users/hanxiantao/IdeaProjects/aspectj-ltw/target/aspectjweaver-1.8.13.jar
By introducing aspectjweaver dependency, go to the local maven warehouse to obtain aspectjweaver-1.8.13.jar
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency>
Operation result :
[AppClassLoader@18b4aac2] info AspectJ Weaver Version 1.8.13 built on Wednesday Nov 15, 2017 at 19:26:44 GMT
[AppClassLoader@18b4aac2] info register classloader sun.misc.Launcher$AppClassLoader@18b4aac2
[AppClassLoader@18b4aac2] info using configuration /Users/hanxiantao/IdeaProjects/aspectj-ltw/target/classes/META-INF/aop.xml
[AppClassLoader@18b4aac2] info register aspect com.ppdai.MethodCostTimeAspect
[AppClassLoader@18b4aac2] weaveinfo Join point 'method-execution(void com.ppdai.AspectjLtwTest.main(java.lang.String[]))' in Type 'com.ppdai.AspectjLtwTest' (AspectjLtwTest.java:12) advised by around advice from 'com.ppdai.MethodCostTimeAspect' (MethodCostTimeAspect.java)
[AppClassLoader@18b4aac2] weaveinfo Join point 'method-execution(void com.ppdai.MethodCostTimeAspect.pointcut())' in Type 'com.ppdai.MethodCostTimeAspect' (MethodCostTimeAspect.java:17) advised by around advice from 'com.ppdai.MethodCostTimeAspect' (MethodCostTimeAspect.java)
void com.ppdai.AspectjLtwTest.main(String[]) 方法耗时: 428ms
The source code has been uploaded to GitHub : https://github.com/hxt970311/aspectj-ltw
Reference :
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
https://www.javadoop.com/post/aspectj#Load-Time%20Weaving