Java模拟Spring声明式事务的核心是:用反射识别@Transactional注解,通过JDK或CGLIB动态代理拦截方法,在invoke或intercept中手动织入Connection获取、setAutoCommit(false)、try-catch-commit/rollback及ThreadLocal连接复用逻辑。

Java 中模拟 Spring 声明式事务的核心,关键不是“重写 Spring”,而是理解并复现其最简可行逻辑:用反射识别注解 + 用代理拦截方法 + 在前后织入事务控制代码。整个过程不依赖 Spring 容器,纯 Java 即可验证原理。
用反射扫描 @Transactional 注解
这是事务触发的起点。Spring 启动时会遍历所有 Bean 类型,而我们可用反射手动完成类似动作:
- 遍历指定包路径下的所有 Class(可用 ClassLoader.getResources 或第三方工具如 Reflections)
- 对每个类调用 clazz.getDeclaredMethods() 获取全部方法
- 对每个方法调用 method.isAnnotationPresent(Transactional.class) 判断是否标注
- 若存在,提取注解属性(如 rollbackFor、propagation),封装为 TransactionConfig 对象备用
注意:反射只能读取 RUNTIME 保留策略的注解,所以自定义注解必须加 @Retention(RetentionPolicy.RUNTIME)。
用动态代理生成事务增强对象
代理是事务逻辑落地的载体。根据目标类是否实现接口,选择不同方式:
立即学习“Java免费学习笔记(深入)”;
- JDK 动态代理:适用于有接口的场景。实现 InvocationHandler,在 invoke() 方法中统一包裹事务逻辑
- CGLIB 代理:适用于无接口的普通类。通过 Enhancer 创建子类,在 intercept() 中插入事务控制
- 代理对象必须持有原始目标实例(target),才能在 try 块中调用 method.invoke(target, args)
代理不是装饰器,而是运行时生成的新对象——调用方拿到的是代理,实际执行的是被代理对象的方法,中间夹着事务切面。
在代理中手动织入事务流程
事务本质就是 JDBC 的几行关键操作。代理的 invoke() 或 intercept() 方法里,需模拟 Spring 的四步闭环:
- 获取 Connection(从 DataSource 或 ThreadLocal 绑定的连接池中取出)
- 执行 conn.setAutoCommit(false) 开启事务边界
- try 块内执行目标方法;若抛出 rollbackFor 指定的异常类型,执行 conn.rollback()
- 否则执行 conn.commit();finally 中确保 conn.close() 或归还连接池
这里没有 Spring 的 PlatformTransactionManager,但 Connection + try-catch-commit/rollback 就是事务最原始的表达。ThreadLocal 可用来保证同一线程内复用同一连接,模拟 Spring 的事务同步机制。
事务传播行为的简易模拟
REQUIRED 是默认行为,实现它只需判断当前线程是否有活跃事务连接:
- 查 ThreadLocal
,若有且未关闭,直接复用,不重复 setAutoCommit(false) - 若无,则新建 Connection 并开启事务 —— 这就是“有则加入,无则新建”的 REQUIRED 语义
- SUPPORTS、REQUIRES_NEW 等可依此类推:前者跳过开启,后者强制挂起旧连接、新建连接
传播逻辑不复杂,难点在于连接生命周期管理与嵌套时的挂起/恢复 —— Spring 用 TransactionSynchronizationManager 实现,我们可用 Map
不复杂但容易忽略


















