java agent和byte-buddy组合的简单使用

2024-02-17 15:32:21 浏览数 (2)

一、java agent和byte-buddy组合的使用场景

java agent开了一扇门,bytebuddy在开的这扇门中打开了一片新的天地。比较典型的就是skywalking、sermant、arthas、mockito、fastjson等。是不是很好奇skywalking、sermant、arthas、mockito、fastjson的原理。下面我们来了解一下。

二、Java Agent技术的框架

Java Agent技术是一种在Java虚拟机(JVM)启动时或运行时,可以插入到JVM中的程序。这种技术主要用于实现一些高级功能,如字节码操作、性能监控、调试、热修复等。

在Java Agent技术的框架下,常用的框架有以下几个:

  1. Byte Buddy: 这是一个强大的库,用于在运行时创建和修改Java类。Byte Buddy提供了一个简单易用的API,用于生成、修改和加载Java字节码。它支持Java 5及更高版本,并且与Java Agent技术非常配合。
  2. ASM: ASM是一个Java字节码操控框架。它能直接生成或以二进制形式修改已有类或者核心类的字节码。ASM可以直接生成字节码,而不需要了解Java虚拟机指令。ASM比其他的Java字节码操控框架(例如Javassist, BCEL, CGLIB)更快更小。
  3. Javassist: Javassist是一个开源的分析、编辑和创建Java字节码的库。它已经被许多其他的Java类库和工具使用,包括Hibernate和Spring。Javassist是分析字节码的工具,并且提供了一个简单的API来操作和生成字节码。
  4. Instrumentation API: 这是Java Agent技术的核心API,用于在运行时修改类的字节码。使用这个API,你可以实现自己的类加载器,并在类被加载到JVM时修改其字节码。
  5. HotSwapAgent: HotSwapAgent是一个Java类重新加载器,它支持在不停止和重启JVM的情况下重新加载已修改的类。HotSwapAgent基于Java Instrumentation API,并提供了更多的功能,如条件断点、类变量查看和修改等。

三、基于java agent和byte-buddy组合简单使用

首先需要准备好premain,然后基于premain中定义的转换器,在转换器中,添加需要拦截的方法,拦截的规则,最终将其安装到Instrumentation。

1)首先准备好java agent的premain
代码语言:javascript复制
public class PreMainAgent {

    public static void premain(String agentArgs, Instrumentation inst) {
        //创建一个转换器,转换器可以修改类的实现
        //ByteBuddy对java agent提供了转换器的实现,直接使用即可
        AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
            public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
                return builder
                        // 拦截任意方法
                        .method(ElementMatchers.<MethodDescription>any())
                        // 拦截到的方法委托给TimeInterceptor
                        .intercept(MethodDelegation.to(MyInterceptor.class));
            }
        };
         new AgentBuilder 
                .Default()
                // 根据包名前缀拦截类
                .type(ElementMatchers.nameStartsWith("com.agent"))
                // 拦截到的类由transformer处理
                .transform(transformer)
                .installOn(inst);
    }
}
2)在pom.xml中新增premain的信息
代码语言:javascript复制
<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <appendAssemblyId>false</appendAssemblyId>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive> <!--自动添加META-INF/MANIFEST.MF -->
            <manifest>
                <!-- 添加 mplementation-*和Specification-*配置项-->
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
            <manifestEntries>
                <!--指定premain方法所在的类-->
                <Premain-Class>com.example.demo.bytecode.PreMainAgent</Premain-Class>
                <!--添加这个即可-->
                <Agent-Class>com.example.demo.bytecode.PreMainAgent</Agent-Class>
                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                <Can-Retransform-Classes>true</Can-Retransform-Classes>
            </manifestEntries>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>
3)准备好拦截器
代码语言:javascript复制
public class MyInterceptor {
    @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.getName()   ":"   (System.currentTimeMillis() - start)    "ms");
        }
    }
}

将其打好包,然后在业务系统中使用即可

4)在业务系统中引入byte-buddy的依赖
代码语言:javascript复制
<dependencies>
    <dependency>
        <groupId>net.bytebuddy</groupId>
        <artifactId>byte-buddy</artifactId>
        <version>1.9.2</version>
    </dependency>
    <dependency>
        <groupId>net.bytebuddy</groupId>
        <artifactId>byte-buddy-agent</artifactId>
        <version>1.9.2</version>
    </dependency>
</dependencies>
5)在业务系统项目中使用

注意:这里的包应该是com.example.biz打头的

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello Byte-Buddy");
    }
}

以上是对byte-buddy的简单入门案例,通过上面的案例可以很好的理解sermant的原理。

参考:

byte-buddy开源地址:https://github.com/raphw/byte-buddy

sermant开源地址:https://github.com/huaweicloud/Sermant

0 人点赞