1、概述
在前面兩節中,咱們實現了Agent,可是其不管在使用方式和功能上面都有必定的侷限性。本文咱們藉助字節碼工具ByteBuddy,寫出高級的Agent。java
ByteBuddy不單單是爲了生成Java-Agent,它提供的API甚至能夠改變重寫一個Java類,本文咱們使用其API實現和第二節同樣的功能,給目標類中的函數統計其調用耗時。apache
2、實現
一、修改pom.xml
本節和上節的不一樣點,主要有兩個。一個是引入ByteBuddy的依賴,另外一個是須要將ByteBuddy的包經過shade打入到Agent中。下面只截取關鍵代碼:maven
<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>
二、實現一個Agent
與以前相同的是,這裏仍然是在premain處進行處理。經過AgentBuilder方法,生成一個Agent。這裏有兩點須要特別說明:其一是在AgentBuilder.type處,這裏能夠指定須要攔截的類;其二是在builder.method處,這裏能夠指定須要攔截的方法。固然其API支持各類isStatic、isPublic等等一系列方式。ide
public class MyAgent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("this is an perform monitor agent."); AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) { return builder .method(ElementMatchers.<MethodDescription>any()) // 攔截任意方法 .intercept(MethodDelegation.to(TimeInterceptor.class)); // 委託 } }; AgentBuilder.Listener listener = new AgentBuilder.Listener() { @Override public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, DynamicType dynamicType) {} @Override public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { } @Override public void onError(String typeName, ClassLoader classLoader, JavaModule module, Throwable throwable) { } @Override public void onComplete(String typeName, ClassLoader classLoader, JavaModule module) { } }; new AgentBuilder .Default() .type(ElementMatchers.nameStartsWith("com.example.demo")) // 指定須要攔截的類 .transform(transformer) .with(listener) .installOn(inst); } }
在上一步實現Transformer
的過程當中,委託了一個TimeInterceptor.class
。下面是其實現方式,整個的try
語句是原有的代碼執行,咱們在以前打了時間戳,並在其結束後,計算並打印了其調用耗時。函數
public class TimeInterceptor { @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 + ": took " + (System.currentTimeMillis() - start) + "ms"); } } }
這裏須要注意的是,咱們定義的包路徑要和Agent中定義的相同,不然Agent沒法Hook到這個類及其方法。工具
package com.example.demo; public class AgentTest { private void fun1() throws Exception { System.out.println("this is fun 1."); Thread.sleep(500); } private void fun2() throws Exception { System.out.println("this is fun 2."); Thread.sleep(500); } public static void main(String[] args) throws Exception { AgentTest test = new AgentTest(); test.fun1(); test.fun2(); } }
結果:ui
this is an perform monitor agent. this is fun 1. private void com.example.demo.AgentTest.fun1() throws java.lang.Exception: took 501ms this is fun 2. 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