press-monster-java/press-monster-test/demo
kazusa 4e0ee42053 测试RestController拦截,后续改为委托方式进行方法增强 2025-07-23 16:46:31 +08:00
..
src/main/java/io/github/kazusa/pressmonster/demo 测试RestController拦截,后续改为委托方式进行方法增强 2025-07-23 16:46:31 +08:00
README.md 测试RestController拦截,后续改为委托方式进行方法增强 2025-07-23 16:46:31 +08:00
pom.xml 测试RestController拦截,后续改为委托方式进行方法增强 2025-07-23 16:46:31 +08:00
run-demo.sh Bytebuddy委托拦截样例 2025-07-23 15:29:18 +08:00

README.md

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种运行选择

  1. 不使用Agent运行对比效果
  2. 使用Agent运行展示拦截效果
  3. 同时运行两种方式进行对比

方式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)

  • 实现beforeMethodafterMethodhandleException方法
  • 提供参数和返回值的格式化输出
  • 记录方法执行时间和性能统计

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: 日志框架

注意事项

  1. Java版本: 支持Java 8+
  2. ClassLoader隔离: Agent代码与应用代码使用不同的ClassLoader
  3. 性能影响: 拦截会带来额外的性能开销,生产环境需要谨慎使用
  4. 异常处理: 拦截器异常不应影响原始方法的执行逻辑

故障排除

常见问题

Q: Agent没有生效看不到拦截日志 A: 检查是否使用了-javaagent参数确保JAR文件路径正确

Q: 编译失败 A: 检查Java版本和Maven配置确保所有依赖都能正确下载

Q: 某些类没有被拦截 A: 检查匹配规则,确认目标类符合预期的匹配条件

Q: 运行时出现ClassNotFoundException A: 使用Maven Shade插件打包所有依赖到Fat JAR

进一步学习


这个项目完整展示了ByteBuddy Agent的核心功能可以作为学习字节码增强和AOP编程的参考示例。