博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Agent简介及使用Byte Buddy和AspectJ LTW监控方法执行耗时
阅读量:3887 次
发布时间:2019-05-23

本文共 7540 字,大约阅读时间需要 25 分钟。

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:
[=
]

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文件路径

4.0.0
com.ppdai
bytebuddy-agent-demo
1.0-SNAPSHOT
net.bytebuddy
byte-buddy
1.8.20
net.bytebuddy
byte-buddy-agent
1.8.20
org.apache.maven.plugins
maven-compiler-plugin
1.8
1.8
UTF-8
org.apache.maven.plugins
maven-jar-plugin
src/main/resources/META-INF/MANIFEST.MF
true

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.0Premain-Class: com.ppdai.agent.MyAgentCan-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:
[=
]

运行结果

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

源码已上传GitHub:https://github.com/hxt970311/bytebuddy-agent-demo

3、使用AspectJ LTW监控方法执行耗时

AspectJ作为AOP编程的完全解决方案,提供了三种织入时机,分别为:

  • compile-time:编译期织入,在编译的时候直接编译出包含织入代码的.class文件
  • post-compile:编译后织入,增强已经编译出来的类
  • load-time:在JVM进行类加载的时候进行织入

引入aspectj相关依赖

org.aspectj
aspectjrt
1.8.13

编写Aspect

@Aspectpublic class MethodCostTimeAspect {
@Pointcut("execution(* com.ppdai..*(..))") public void pointcut() {
} @Around("pointcut()") public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); System.out.println(joinPoint.getSignature() + " 方法耗时: " + (System.currentTimeMillis() - start) + "ms"); return result; }}

src/main/resources目录下添加META-INF/aop.xml文件,指定Aspect类和需要被织入的类,内容如下:

测试类

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

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

-javaagent:/Users/hanxiantao/IdeaProjects/aspectj-ltw/target/aspectjweaver-1.8.13.jar

通过引入aspectjweaver依赖,到本地maven仓库获取aspectjweaver-1.8.13.jar

org.aspectj
aspectjweaver
1.8.13

运行结果

[AppClassLoader@18b4aac2] info AspectJ Weaver Version 1.8.13 built on Wednesday Nov 15, 2017 at 19:26:44 GMT[AppClassLoader@18b4aac2] info register classloader sun.misc.Launcher$AppClassLoader@18b4aac2[AppClassLoader@18b4aac2] info using configuration /Users/hanxiantao/IdeaProjects/aspectj-ltw/target/classes/META-INF/aop.xml[AppClassLoader@18b4aac2] info register aspect com.ppdai.MethodCostTimeAspect[AppClassLoader@18b4aac2] weaveinfo Join point 'method-execution(void com.ppdai.AspectjLtwTest.main(java.lang.String[]))' in Type 'com.ppdai.AspectjLtwTest' (AspectjLtwTest.java:12) advised by around advice from 'com.ppdai.MethodCostTimeAspect' (MethodCostTimeAspect.java)[AppClassLoader@18b4aac2] weaveinfo Join point 'method-execution(void com.ppdai.MethodCostTimeAspect.pointcut())' in Type 'com.ppdai.MethodCostTimeAspect' (MethodCostTimeAspect.java:17) advised by around advice from 'com.ppdai.MethodCostTimeAspect' (MethodCostTimeAspect.java)void com.ppdai.AspectjLtwTest.main(String[]) 方法耗时: 428ms

源码已上传GitHub:https://github.com/hxt970311/aspectj-ltw

参考

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

https://www.javadoop.com/post/aspectj#Load-Time%20Weaving

你可能感兴趣的文章
数据结构之图(存储结构、遍历)
查看>>
使用sizeof计算类的大小
查看>>
乐观锁与悲观锁——解决并发问题
查看>>
operator 类型转换及重载
查看>>
HTTP状态码
查看>>
TCP/IP详解--举例明白发送/接收缓冲区、滑动窗口协议之间的关系
查看>>
TCP/IP详解--再次深入理解TCP网络编程中的send和recv
查看>>
TCP与UDP收发的时候TCP有缓冲区还是UDP有缓冲区,使用它们时该注意什么?
查看>>
C++中map、hash_map、unordered_map、unordered_set通俗辨析
查看>>
clone的fork与pthread_create创建线程有何不同&pthread多线程编程的学习小结
查看>>
运算符重载参数的顺序对运算是否有影响
查看>>
什么时候要用虚析构函数?
查看>>
序列化、反序列化与jsoncpp学习
查看>>
同步/异步与阻塞非阻塞的关系
查看>>
epoll模型讲解/源码分析
查看>>
ELF格式与bss段
查看>>
java继承 long和float小记点
查看>>
记录几点在开发中遇到的问题 2015-7-28 (会更新)
查看>>
网银在线的异步操作代码示意图
查看>>
火狐Firefox浏览器安装Selenium_IDE的步骤以及其使用规则
查看>>