【Agent】ByteBuddy 增加方法执行耗时

1  前言

上节我们使用javassist字节码增强的方式,来监控方法程序的执行耗时。这节我们再用一个字节码操作工具ByteBuddy来帮助我们实现更完善的监控程序。

2  Java agent

2.1  ByteBuddy 是什么

Byte Buddy是一个字节码生成和操作库,用于在Java应用程序运行时创建和修改Java类,而无需编译器的帮助。

除了Java类库附带的代码生成实用程序外,Byte Buddy还允许创建任意类,并且不限于实现用于创建运行时代理的接口。

此外,Byte Buddy提供了一种方便的API,可以使用Java代理或在构建过程中手动更改类。

无需理解字节码指令,即可使用简单的 API 就能很容易操作字节码,控制类和方法。

已支持Java 11,库轻量,仅取决于Java字节代码解析器库ASM的访问者API,它本身不需要任何其他依赖项。
比起JDK动态代理、cglib、Javassist,Byte Buddy在性能上具有一定的优势。

就像它的官网介绍;

Byte Buddy 是一个代码生成和操作库,用于在 Java 应用程序运行时创建和修改 Java 类,而无需编译器的帮助。除了 Java 类库附带的代码生成实用程序外,Byte Buddy 还允许创建任意类,并且不限于实现用于创建运行时代理的接口。

此外,Byte Buddy 提供了一种方便的 API,可以使用 Java 代理或在构建过程中手动更改类。

    • 无需理解字节码指令,即可使用简单的 API 就能很容易操作字节码,控制类和方法。
    • 已支持Java 11,库轻量,仅取决于Java字节代码解析器库ASM的访问者API,它本身不需要任何其他依赖项。
    • 比起JDK动态代理、cglib、Javassist,Byte Buddy在性能上具有一定的优势。

 另外关于字节码的增强工具的对比:

对比ASMJavassistJDK ProxyCglibByteBuddy
起源时间 2002 1999 2000 2011 2014
包大小 130KB (版本9.3) 788KB (版本3.28.0-GA)     3.7MB (版本1.10.19)
增强方式 字节码指令 字节码指令和源码(注:源码文本) 源码 源码 源码
源码编译 NA 不支持 支持 支持 支持
agent支持 支持 支持 不支持,依赖框架 不支持,依赖框架 支持
性能
维护状态 停止升级 停止维护 活跃
优点 超高性能,应用场景广泛 同时支持字节码指令和源码两种增强方式 JDK原生类库支持   零侵入,提供良好的API扩展编程
缺点 字节码指令对应用开发者不友好   场景非常局限,只适用于Java接口 已经不再维护,对于新版JDK17+支持不好,官网建议切换到ByteBuddy  
应用场景 小,高性能,广泛用于语言级别     广泛用于框架场景 广泛用于Trace场景

 

2.2  尝试

首先加入依赖:

复制代码
<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>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <artifactSet>
            <includes>
                <include>javassist:javassist:jar:</include>
                <include>net.bytebuddy:byte-buddy:jar:</include>
                <include>net.bytebuddy:byte-buddy-agent:jar:</include>
            </includes>
        </artifactSet>
    </configuration>
</plugin>
复制代码

MyAgent.java

复制代码
/**
 * @author: kuku
 * @description
 */
public class MyAgent {

    /**
     * JVM 首先尝试在代理类上调用以下方法
     * @param agentArgs
     * @param inst
     */
    public static void premain(String agentArgs, Instrumentation inst) {
        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
            return 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.virtuous")) // 指定需要拦截的类
                .transform(transformer)
                .with(listener)
                .installOn(inst);
    }

    /**
     * 如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法
     * @param agentArgs
     */
    public static void premain(String agentArgs) {
        System.out.println("premain方法2" + agentArgs);
    }

}
复制代码

MethodCostTime.java

复制代码
/**
 * @author kuku
 */
public class MethodCostTime {

    @RuntimeType
    public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
        long start = System.nanoTime();
        try {
            // 原有函数执行
            return callable.call();
        } finally {
            System.out.println(method + " 方法耗时: " + (System.nanoTime() - start) + "ns");
        }
    }

}
复制代码

测试执行效果:

3  小结

好啦,本节就到这里,有理解不对的地方欢迎指正哈。

posted @   酷酷-  阅读(268)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2023-06-06 【Redis】【持久化】RDB 快照是怎么实现的?
2023-06-06 【Redis】【持久化】AOF 持久化是怎么实现的?
2023-06-06 【Redis】Redis 数据类型详解
2023-06-06 【网络基础】Linux 系统是如何收发网络包的?
2023-06-06 【网络基础】键入网址到网页显示,期间发生了什么?
2023-06-06 【网络基础】TCP/IP 网络模型有哪几层?
点击右上角即可分享
微信分享提示