Java字节码3使用ByteBuddy实现一个JavaAgent

编程

Java字节码系列
Java字节码1-Agent简单上手
Java字节码2-instrument初体验
Java字节码3-使用ByteBuddy实现一个Java-Agent
Java字节码4-使用Java-Agent实现一个JVM监控工具
本系列代码可见:https://github.com/hawkingfoo/demo-agent

一、概述

在前面两节中,我们实现了Agent,但是其无论在使用方式和功能上面都有一定的局限性。本文我们借助字节码工具ByteBuddy,写出高级的Agent。

ByteBuddy不仅仅是为了生成Java-Agent,它提供的API甚至可以改变重写一个Java类,本文我们使用其API实现和第二节一样的功能,给目标类中的函数统计其调用耗时。

二、实现

1、修改pom.xml

本节和上节的不同点,主要有两个。一个是引入ByteBuddy的依赖,另一个是需要将ByteBuddy的包通过shade打入到Agent中。下面只截取关键代码:

<dependency>

<groupId>net.bytebuddy</groupId>

<artifactId>byte-buddy</artifactId>

<version>1.5.7</version>

</dependency>

<dependency>

<groupId>net.bytebuddy</groupId>

<artifactId>byte-buddy-agent</artifactId>

<version>1.5.7</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>

2、实现一个Agent

与之前相同的是,这里仍然是在premain处进行处理。通过AgentBuilder方法,生成一个Agent。这里有两点需要特别说明:其一是在AgentBuilder.type处,这里可以指定需要拦截的类;其二是在builder.method处,这里可以指定需要拦截的方法。当然其API支持各种isStatic、isPublic等等一系列方式。

publicclassMyAgent{

publicstaticvoidpremain(String agentArgs,Instrumentation inst){

System.out.println("this is an perform monitor agent.");

AgentBuilder.Transformer transformer =newAgentBuilder.Transformer(){

@Override

publicDynamicType.Builder<?>transform(DynamicType.Builder<?> builder,

TypeDescription typeDescription,

ClassLoader classLoader){

return builder

.method(ElementMatchers.<MethodDescription>any())// 拦截任意方法

.intercept(MethodDelegation.to(TimeInterceptor.class));// 委托

}

};

AgentBuilder.Listener listener =newAgentBuilder.Listener(){

@Override

publicvoidonTransformation(TypeDescription typeDescription,ClassLoader classLoader,JavaModulemodule,DynamicType dynamicType){}

@Override

publicvoidonIgnored(TypeDescription typeDescription,ClassLoader classLoader,JavaModulemodule){}

@Override

publicvoidonError(String typeName,ClassLoader classLoader,JavaModulemodule,Throwable throwable){}

@Override

publicvoidonComplete(String typeName,ClassLoader classLoader,JavaModulemodule){}

};

newAgentBuilder

.Default()

.type(ElementMatchers.nameStartsWith("com.example.demo"))// 指定需要拦截的类

.transform(transformer)

.with(listener)

.installOn(inst);

}

}

3、实现一个用来委托的Interceptor

在上一步实现Transformer的过程中,委托了一个TimeInterceptor.class。下面是其实现方式,整个的try语句是原有的代码执行,我们在之前打了时间戳,并在其结束后,计算并打印了其调用耗时。

publicclassTimeInterceptor{

@RuntimeType

publicstaticObjectintercept(@OriginMethod method,

@SuperCallCallable<?> callable)throwsException{

long start =System.currentTimeMillis();

try{

// 原有函数执行

return callable.call();

}finally{

System.out.println(method +": took "+(System.currentTimeMillis()- start)+"ms");

}

}

}

三、运行

这里需要注意的是,我们定义的包路径要和Agent中定义的相同,否则Agent无法Hook到这个类及其方法。

packagecom.example.demo;

publicclassAgentTest{

privatevoidfun1()throwsException{

System.out.println("this is fun 1.");

Thread.sleep(500);

}

privatevoidfun2()throwsException{

System.out.println("this is fun 2.");

Thread.sleep(500);

}

publicstaticvoidmain(String[] args)throwsException{

AgentTest test =newAgentTest();

test.fun1();

test.fun2();

}

}

结果:

thisis an perform monitor agent.

thisisfun1.

private void com.example.demo.AgentTest.fun1() throws java.lang.Exception: took 501ms

thisisfun2.

private void com.example.demo.AgentTest.fun2() throws java.lang.Exception: took 500ms

public static void com.example.demo.AgentTest.main(java.lang.String[]) throws java.lang.Exception: took 1001ms

可以看到,我们的Agent成功Hook并增强了其调用方法。

https://notes.diguage.com/byte-buddy-tutorial/#preliminary



 

以上是 Java字节码3使用ByteBuddy实现一个JavaAgent 的全部内容, 来源链接: utcz.com/z/512825.html

回到顶部