7.5 KiB
7.5 KiB
ByteBuddy Agent 拦截演示项目
项目概述
这是一个完整的ByteBuddy Java Agent实现示例,展示了如何使用AgentBuilder进行字节码增强和方法拦截。项目演示了多种目标类匹配方式,包括类名匹配、注解匹配、包路径匹配等。
核心特性
✅ 使用AgentBuilder方式进行字节码增强
✅ 使用委托方式对目标方法增强
✅ 设定用来增强方法的拦截器
✅ 在委托类中使用拦截器对目标方法前后增强
✅ 完成能在方法调用前后打印入参和返回结果的插件
✅ 展示多种目标类匹配方式(类名、注解、包路径等)
项目结构
src/main/java/io/github/kazusa/pressmonster/demo/
├── agent/ # Agent核心代码
│ ├── SimpleAgent.java # Agent主类,展示多种匹配方式
│ ├── MethodInterceptor.java # 拦截器接口
│ ├── LoggingInterceptor.java # 具体的日志拦截器实现
│ └── DelegateMethodHandler.java # 委托方法处理器
├── annotation/ # 自定义注解
│ └── Monitored.java # 用于注解匹配演示
├── service/ # 示例服务类
│ ├── UserService.java # 普通服务类,用于类名匹配
│ └── OrderService.java # 带注解的服务类,用于注解匹配
├── DemoApplication.java # 演示应用主类
├── SampleController.java # Spring Controller示例
└── run-demo.sh # 便捷运行脚本
拦截匹配方式
1. 精确类名匹配
named("io.github.kazusa.pressmonster.plugins.web.SampleController")
2. Spring注解匹配
isAnnotatedWith(RestController.class)
3. 自定义注解匹配
isAnnotatedWith(named("io.github.kazusa.pressmonster.demo.annotation.Monitored"))
4. 包路径前缀匹配
nameStartsWith("io.github.kazusa.pressmonster.demo.service")
5. 类名后缀匹配
nameEndsWith("Service")
6. 类名包含匹配
nameContains("Controller")
7. 复合条件匹配
// 使用OR条件组合多种匹配方式
named("className")
.or(isAnnotatedWith(SomeAnnotation.class))
.or(nameStartsWith("packagePrefix"))
.and(not(nameContains("Test"))) // 排除测试类
快速开始
方式1: 使用运行脚本(推荐)
# 直接运行脚本
./run-demo.sh
# 或者
bash run-demo.sh
脚本会自动编译、打包,并提供3种运行选择:
- 不使用Agent运行(对比效果)
- 使用Agent运行(展示拦截效果)
- 同时运行两种方式进行对比
方式2: 手动编译运行
# 1. 编译和打包
mvn clean package -DskipTests
# 2. 不使用Agent运行(看不到拦截日志)
java -jar target/demo-1.0.0-SNAPSHOT.jar
# 3. 使用Agent运行(可以看到拦截日志)
java -javaagent:target/demo-1.0.0-SNAPSHOT.jar -jar target/demo-1.0.0-SNAPSHOT.jar
预期输出示例
使用Agent运行时的输出
========================================
Simple ByteBuddy Agent 启动中...
Agent参数: 无
========================================
[AGENT] 成功增强类: io.github.kazusa.pressmonster.plugins.web.SampleController (匹配方式: SPRING_CONTROLLER)
[AGENT] 成功增强类: io.github.kazusa.pressmonster.demo.service.UserService (匹配方式: SERVICE_PACKAGE)
[AGENT] 成功增强类: io.github.kazusa.pressmonster.demo.service.OrderService (匹配方式: SERVICE_PACKAGE)
[AGENT] ByteBuddy Agent 安装完成!
========================================
=== ByteBuddy Agent 拦截演示应用 ===
========================================
🔍 测试1: @RestController注解匹配
目标类: SampleController
匹配规则: isAnnotatedWith(RestController.class)
----------------------------------------
[INTERCEPT] [SPRING_CONTROLLER] Before method: SampleController.getUsers()
[INTERCEPT] [SPRING_CONTROLLER] Arguments: []
SampleController.getUsers() 执行中...
[INTERCEPT] [SPRING_CONTROLLER] After method: SampleController.getUsers()
[INTERCEPT] [SPRING_CONTROLLER] Return: {users=[user1, user2, user3], total=3}
[INTERCEPT] [SPRING_CONTROLLER] Duration: 15.23ms
[INTERCEPT] [SPRING_CONTROLLER] ==========================================
核心实现原理
1. Agent入口点 (SimpleAgent.java)
- 实现
premain方法作为Agent入口 - 配置AgentBuilder的匹配规则和转换逻辑
- 使用
MethodDelegation.to()进行方法委托
2. 委托处理器 (DelegateMethodHandler.java)
- 使用
@RuntimeType注解支持动态类型匹配 - 集成拦截器调用逻辑
- 处理原方法执行和异常捕获
3. 拦截器 (LoggingInterceptor.java)
- 实现
beforeMethod、afterMethod、handleException方法 - 提供参数和返回值的格式化输出
- 记录方法执行时间和性能统计
4. Maven配置
- 配置
Premain-Class和相关Agent属性 - 使用Shade插件打包依赖到Fat JAR
- 支持同时作为普通应用和Agent运行
扩展功能
添加新的匹配规则
在SimpleAgent.buildTypeMatchers()方法中添加新的匹配条件:
.or(nameMatches(".*Controller.*")) // 正则表达式匹配
.or(isInterface()) // 匹配接口
.or(hasSuperType(named("某个父类"))) // 匹配特定父类的子类
自定义拦截器
实现MethodInterceptor接口创建自定义拦截器:
public class CustomInterceptor implements MethodInterceptor {
@Override
public void beforeMethod(Object target, Method method, Object[] args, String matchType) {
// 自定义前置逻辑
}
@Override
public Object afterMethod(Object target, Method method, Object[] args, Object result,
String matchType, long startTime) {
// 自定义后置逻辑
return result;
}
@Override
public void handleException(Object target, Method method, Object[] args,
Throwable throwable, String matchType) {
// 自定义异常处理逻辑
}
}
依赖说明
- ByteBuddy: 1.14.9 - 字节码操作核心库
- Spring Web: 用于@RestController注解支持
- Jackson: JSON序列化(可选)
- SLF4J + Logback: 日志框架
注意事项
- Java版本: 支持Java 8+
- ClassLoader隔离: Agent代码与应用代码使用不同的ClassLoader
- 性能影响: 拦截会带来额外的性能开销,生产环境需要谨慎使用
- 异常处理: 拦截器异常不应影响原始方法的执行逻辑
故障排除
常见问题
Q: Agent没有生效,看不到拦截日志
A: 检查是否使用了-javaagent参数,确保JAR文件路径正确
Q: 编译失败 A: 检查Java版本和Maven配置,确保所有依赖都能正确下载
Q: 某些类没有被拦截 A: 检查匹配规则,确认目标类符合预期的匹配条件
Q: 运行时出现ClassNotFoundException A: 使用Maven Shade插件打包所有依赖到Fat JAR
进一步学习
这个项目完整展示了ByteBuddy Agent的核心功能,可以作为学习字节码增强和AOP编程的参考示例。