实测 CGLIB 代理的限制:final 方法不被拦截而且在字段未初始化的代理上执行(读到 null);private 与 static 方法不单独拦截;靠 Objenesis 构造器只调用 1 次(关掉后调用 2 次,只有带参构造器的类会失败);final 类抛 Cannot subclass final class。
实测 @Async:纯 Spring 默认每次调用新建一个线程(100 次调用 100 个线程),Boot 默认 8 个线程的 applicationTaskExecutor;void 方法的异常默认只记 ERROR 日志;返回 String 的 @Async 方法直接抛 IllegalArgumentException;ThreadLocal 不会带到异步线程,TaskDecorator 可以传递;执行器满了默认拒绝 6 个任务、CallerRunsPolicy 让调用者自己跑。
实测 @Scheduled:默认调度器只有一个线程(纯 Spring 的 pool-1-thread-1、Boot 的 scheduling-1),一个耗时 1000 ms 的慢任务让每 200 ms 的快任务错过 5 次并在结束后 30 ms 内补跑 5 次;线程池设成 3 就不互相挡;fixedRate 与 fixedDelay 在任务比间隔慢时周期约 506 ms 对约 806 ms;任务抛异常不影响后续调度;加 @Async 会重叠执行。
实测 Spring 缓存抽象:默认键只由参数决定(不含方法名,同缓存同参数的不同方法会冲突);null 默认也被缓存;8 个线程并发取同一个键,没有 sync 目标方法执行 8 次、sync = true 只执行 1 次;@CacheEvict 在方法抛异常时默认不清缓存;ConcurrentMapCacheManager 缓存的是引用;自调用不走缓存。
实测 Spring Framework 7 内置的 @Retryable:默认 maxRetries = 3、delay = 1000 ms(执行时刻 5、1014、2019 ms);multiplier = 2、maxDelay = 500 时间隔 100、200、400、500、500;includes / excludes / predicate 筛选异常(嵌套的 cause 也参与匹配);次数用完抛原始异常,timeout 把 maxRetries = 10 截断成 3 次;RetryTemplate 次数用完抛 RetryException;@ConcurrencyLimit(2) 把最大并发限制成 2。
实测怎么从代理拿回原始对象与类:AopUtils.getTargetClass 只脱一层,AopProxyUtils.ultimateTargetClass 一路脱到底,ClassUtils.getUserClass 不处理 JDK 代理;CGLIB 代理类上 getAnnotation 找不到目标类注解(findAnnotation 才行);代理与目标 equals、hashCode 都不相等;两个切面加 @Async 合并成同一个代理的 4 个拦截器。
实测 @Transactional 的实现:事务管理器依次 doGetTransaction、doBegin(拿连接、关 autocommit、绑定线程)、doCommit 或 doRollback、doCleanupAfterCompletion;事务里两条 insert 用同一个连接(connection_id 都是 12);MySQL general_log 里一个事务是 SET autocommit=0 … COMMIT … SET autocommit=1 共 7 条,没有 @Transactional 的两条 insert 只有两条语句本身。
实测事务传播行为 7 种:外面有事务时 REQUIRED、NESTED、SUPPORTS、MANDATORY 加入同一个连接,REQUIRES_NEW 与 NOT_SUPPORTED 用另一个连接,NEVER 抛异常;外层回滚时 REQUIRES_NEW 与 NOT_SUPPORTED 的行保留;inner 抛异常被外层吞掉时 REQUIRED 在提交时抛 UnexpectedRollbackException,REQUIRES_NEW 与 NESTED 只回滚自己;外面没有事务时 MANDATORY 抛异常。
实测事务回滚规则:RuntimeException 与 Error 回滚,受检异常 IOException 默认提交(表里剩 1 行);rollbackFor 才会回滚;方法内部吞掉异常就提交,setRollbackOnly() 可以手动回滚;rollbackFor 与 noRollbackFor 同时匹配时继承层次上更近的一条赢。
实测 @Transactional 不生效的场景:自调用、private、final、吞掉异常、事务里开线程、事务管理器绑错数据库、MyISAM 表、new 出来的对象都不回滚;Spring 7 的 CGLIB 代理下 package-private 与 protected 的方法是生效的;默认 JDK 代理只能按接口取 Bean。
实测 readOnly = true 的效果:JDBC 一侧靠 SET SESSION TRANSACTION READ ONLY 开始、READ WRITE 结束,里面用 JdbcTemplate 写会被驱动在客户端拒绝;Hibernate 一侧实体修改不再刷到数据库(没有 UPDATE,手动 em.flush() 也不报错,修改悄悄丢掉);加入外层读写事务时内层的 readOnly 无效。
实测事务资源绑定在线程上:事务内线程上绑着 HikariDataSource → ConnectionHolder,子线程里事务不活跃、资源 0 个;同步回调提交时依次是 beforeCommit、beforeCompletion、afterCommit、afterCompletion(COMMITTED),回滚时没有 beforeCommit 和 afterCommit;afterCommit 抛异常撤销不了提交;绕过 Spring 直接 ds.getConnection() 拿到另一个连接,写入不会回滚。