朱晔和你聊Spring系列S1E6:容易犯错的Spring AOP (4)

然后需要配置Spring来使用ASPECTJ的增强方式来做事务管理:
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
public class Spring101AopApplication implements CommandLineRunner {
重新使用maven编译代码后可以看到,相关代码已经变了样:

@Transactional( rollbackFor = {Exception.class} ) public void _insertData(boolean success) { AnnotationTransactionAspect var10000 = AnnotationTransactionAspect.aspectOf(); Object[] var3 = new Object[]{this, Conversions.booleanObject(success)}; var10000.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(this, new MyServiceImpl$AjcClosure1(var3), ajc$tjp_0); } public void insertData(boolean success) { try { this._insertData(success); } catch (Exception var3) { var3.printStackTrace(); } System.out.println("记录数:" + this.dbMapper.personCount()); }

运行程序可以看到如下日志:

2018-10-07 09:35:12.360 DEBUG 19459 --- [ main] o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@1169317628 wrapping conn0: url=jdbc:h2:mem:testdb user=SA] will be managed by Spring

而且最后输出的结果是2,说明第二次插入数据整体回滚了。
如果使用IDEA的话还可以配置先由javac编译再由ajc后处理,具体参见IDEA官网这里不详述。

朱晔和你聊Spring系列S1E6:容易犯错的Spring AOP

使用AOP进行事务后处理

我们先使用刚才说的方法3改造一下代码,使得Spring AOP可以处理事务(Aspject AOP功能虽然强大但是和Spring结合的不好,所以我们接下去的测试还是使用Spring AOP),删除aspjectj相关依赖,在IDEA配置回javac编译器重新编译项目。本节中我们尝试建立第一个我们的切面:

package me.josephzhu.spring101aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; @Aspect @Component @Slf4j class TransactionalAspect extends TransactionSynchronizationAdapter { @Autowired private DbMapper dbMapper; private ThreadLocal<JoinPoint> joinPoint = new ThreadLocal<>(); @Before("@within(org.springframework.transaction.annotation.Transactional) || @annotation(org.springframework.transaction.annotation.Transactional)") public void registerSynchronization(JoinPoint jp) { joinPoint.set(jp); TransactionSynchronizationManager.registerSynchronization(this); } @Override public void afterCompletion(int status) { log.info(String.format("【%s】【%s】事务提交 %s,目前记录数:%s", joinPoint.get().getSignature().getDeclaringType().toString(), joinPoint.get().getSignature().toLongString(), status == 0 ? "成功":"失败", dbMapper.personCount())); joinPoint.remove(); } }

在这里,我们的切点是所有标记了@Transactional注解的类以及标记了@Transactional注解的方法,我们的增强比较简单,在事务同步管理器注册一个回调方法,用于事务完成后进行额外的处理。这里的一个坑是Spring如何实例化切面。通过查文档或做实验可以得知,默认情况下TranscationalAspect是单例的,在多线程情况下,可能会有并发,保险起见我们使用ThreadLocal来存放。运行代码后可以看到如下输出:

2018-10-07 10:01:32.384 INFO 19599 --- [ main] m.j.spring101aop.TransactionalAspect : 【class me.josephzhu.spring101aop.MyServiceImpl】【public void me.josephzhu.spring101aop.MyServiceImpl.insertData(boolean)】事务提交 成功,目前记录数:2 2018-10-07 10:01:32.385 DEBUG 19599 --- [ main] o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@1430104337 wrapping conn0: url=jdbc:h2:mem:testdb user=SA] will be managed by Spring 2018-10-07 10:01:32.449 DEBUG 19599 --- [ main] o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@1430104337 wrapping conn0: url=jdbc:h2:mem:testdb user=SA] will be managed by Spring 2018-10-07 10:01:32.449 INFO 19599 --- [ main] m.j.spring101aop.TransactionalAspect : 【class me.josephzhu.spring101aop.MyServiceImpl】【public void me.josephzhu.spring101aop.MyServiceImpl.insertData(boolean)】事务提交 失败,目前记录数:2

可以看到Spring AOP做了事务管理,我们两次事务提交第一次成功第二次失败,失败后记录数还是2。这个功能还可以通过Spring的@TransactionalEventListener注解实现,这里不详述。

切换JDK代理和CGLIB代理

我们现在注入的是接口,我们知道对于这种情况Spring AOP应该使用的是JDK代理。但是SpringBoot默认开启了下面的属性来全局启用CGLIB代理:

spring.aop.proxy-target-class=true

我们尝试把这个属性设置成false,然后在刚才的TransationalAspect中的增强方法设置断点,可以看到这是一个ReflectiveMethodInvocation:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzfgzw.html