关于spring事务和spring aop
1. spring 事务默认只有抛出unchecked Exception才会回滚
UncheckedException包括error和runtimeException派生出的所有子类
2. 什么时候才用事务?
对数据库的数据进行批量或连表操作时,为了保证数据的一致性和正确性,我们需要添加事务管理机制进行管理。当对数据库的数据进行操作失败时,事务管理可以很好保证所有的数据回滚到原来的数据,如果操作成功,则保证所有需要更新的数据持久化。
例如:1.转账:A对B转账,需要先把A的钱减掉再把B的钱加上,这两个操作任何一个失败,都要撤销整个转账过程,不然会出大问题。
2.向数据库中插入订单时,先插入订单再插入订单项,这两个操作任何一个失败都会影响数据的一致性,所以必须做事务的处理
3. Spring事务的配置
3.1. 声明式事务(不需要开启注解代理等等)
3.1.1. 配置通知(说明哪些方法需要拦截,被拦截的方法将应用配置好的事务属性)
3.1.2. 配置切入点需要对哪个类进行事务管理
注意:只会管理通知中有的那些方法
例如:
1.<!--通知 -->
<tx:adviceid="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--传播行为 -->
<tx:methodname="save*" propagation="REQUIRED" />
<tx:methodname="*” read-only="true">
</tx:attributes>
</tx:advice>
2.<!—针对通知配置切入点-->
<aop:config>
<!—aop:advisor是针对通知的,如果自己写切面用aop:aspect-->
<aop:advisoradvice-ref="txAdvice"
pointcut="execution(*com.outsider.nh.*.service.*.*(..))" />
</aop:config>
注:上面的配置就是说会对所有com.outsider.*.service下面的类中的save*方法进行事务管理,而对其他方法只读,这种方法配置起来比较多,可能会针对每个service来配置一个通知和切入端
3.2. 注解事务(这时候不需要再配置第一种方法的那些东西了)
3.2.1. 开启注解事务扫描
<tx:annotation-driventransaction-manager="transactionManager"proxy-target-class="true"/>
3.2.2. 使用@Transactional在类上或者方法上注解同时可以配置传播行为和隔离级别
在类上配置时,代表会对本类中所有public的方法进行事务管理
4. Spring事务和spring aop
Spring事务使用了aop这种思想来处理事务,不用我们自己去实现切面类
Spring aop是我们自己写切面来对一些方法做拦截,增强,比如打印日志
5. Spring aop的配置
写切面,主要实现增强方法,包括前置,后置,环绕等等,需要什么类型就实现,
5.1. 配置方式
<aop:config>
<aop:aspect ref="切面bean可以注解扫描进来也可以自己配置bean”>
//配置了一个后置通知,method就是增强代码,也是切面中的一个方法,切入点是那些需要被增强的方法
<aop:aftermethod="synchIndexForArticleAdd" pointcut="execution(*com.colin.service.impl.ArticleServiceImpl.add*(..))"/>
</aop:aspect>
</aop:config>
5.2. 注解方式
5.2.1. 开启aop自动代理
<aop:aspectj-autoproxy />
关于这个配置详情https://blog.csdn.net/z69183787/article/details/18675145
5.2.2. 写切面类 典型的如下
//声明这是一个组件 @Component //声明这是一个切面Bean @Aspect public class ServiceAspect { private final static Log log = LogFactory.getLog(ServiceAspect.class); //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点 @Pointcut("execution(* com.hyq.aop..*(..))") public void aspect(){ } /* * 配置前置通知,使用在方法aspect()上注册的切入点 * 同时接受JoinPoint切入点对象,可以没有该参数 */ @Before("aspect()") public void before(JoinPoint joinPoint){ System.out.println("执行before....."); } //配置后置通知,使用在方法aspect()上注册的切入点 @After("aspect()") public void after(JoinPoint joinPoint){ System.out.println("执行after....."); } //配置环绕通知,使用在方法aspect()上注册的切入点 @Around("aspect()") public void around(JoinPoint joinPoint){ long start = System.currentTimeMillis(); try { ((ProceedingJoinPoint) joinPoint).proceed(); long end = System.currentTimeMillis(); if(log.isInfoEnabled()){ log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!"); } } catch (Throwable e) { long end = System.currentTimeMillis(); if(log.isInfoEnabled()){ log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage()); } } } //配置后置返回通知,使用在方法aspect()上注册的切入点 @AfterReturning("aspect()") public void afterReturn(JoinPoint joinPoint){ if(log.isInfoEnabled()){ log.info("afterReturn " + joinPoint); } } //配置抛出异常后通知,使用在方法aspect()上注册的切入点 @AfterThrowing(pointcut="aspect()", throwing="ex") public void afterThrow(JoinPoint joinPoint, Exception ex){ if(log.isInfoEnabled()){ log.info("afterThrow " + joinPoint + "\t" + ex.getMessage()); } } } |
注意:扫描service层bean时不要扫描到Controller,不然会出现事务无作用!