Java Agent简介及使用Byte Buddy监控方法执行耗时

1、什么是Java Agent

Java Agent提供了一种在加载字节码时,对字节码进行修改的方法。一共有两种方式执行:一种是在main方法执行之前,通过premain来实现;另一种是在程序运行中,通过attach api来实现

1)、Instrumentation

Instrumentation是JDK1.5提供的API,用于拦截类加载事件,并对字节码进行修改,它的主要方法如下:

public interface Instrumentation {
    
    
  	
  	//注册一个转换器,类加载事件会被注册的转换器所拦截
    void
    addTransformer(ClassFileTransformer transformer, boolean canRetransform);
  	
  	//重新触发类加载
    void
    retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
  	
  	//直接替换类的定义
    void
    redefineClasses(ClassDefinition... definitions)
        throws  ClassNotFoundException, UnmodifiableClassException;  

2)、premain()方法

premain()方法是在main()方法之前运行的方法,运行时需要将agent程序打包成jar包,并在启动时添加命令来执行:

-javaagent:<jarpath>[=<options>]

premain()共提供以下两种重载方法,JVM启动时会先尝试使用第一种方法,若没有会使用第二种方法

    public static void premain(String agentArgs, Instrumentation inst)

    public static void premain(String agentArgs)

2、使用Byte Buddy监控方法执行耗时

Byte Buddy是一个代码生成和操作库,用于在Java应用程序运行时创建和修改Java类,而无需编译器的帮助。除了Java类库附带的代码生成实用程序外,Byte Buddy还允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy提供了一种方便的API,可以使用Java代理或在构建过程中手动更改类

pom.xml中引入Byte Buddy相关依赖,并指定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目录下添加META-INF/MANIFEST.MF文件,内容如下:

Manifest-Version: 1.0
Premain-Class: com.ppdai.agent.MyAgent
Can-Redefine-Classes: true

使用mvn clean package打包

测试类

public class AgentTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread.sleep(new Random().nextInt(500));
    }
}

运行测试类时需要指定agent的jar包路径

-javaagent:<jarpath>[=<options>]

运行结果

MyAgent init...
public static void com.ppdai.test.AgentTest.main(java.lang.String[]) throws java.lang.InterruptedException 方法耗时: 350ms

参考

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

猜你喜欢

转载自blog.csdn.net/qq_40378034/article/details/115277963