Introduction to Java Agent and time-consuming execution using Byte Buddy and AspectJ LTW monitoring methods

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/resourcesAdd a META-INF/MANIFEST.MFfile 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 packagepackaging

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/resourcesAdd a META-INF/aop.xmlfile 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

Guess you like

Origin blog.csdn.net/qq_40378034/article/details/115281684