AOP(七) 事务管理 事务 是一组操作的集合,是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败
@Transactional 该注解放于业务(service)层的方法上,类上,接口上。
作用 :将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务。
范例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //方法 @Transactional @Override public void deleteById(Integer id) { deptMapper.deleteById(id); int i=1/0; empMapper.deleteByDeptId(id); } //接口 @Transactional public interface DeptService{} //类 @Transactional @Service public class DeptServiceImpl implements DeptService {} //application.yml #spring事务管理日志 logging: level: org.springframework.jdbc.support.JdbcTransactionManager: debug
rollbackFor 默认情况下,只有出现RuntimeException才会回滚日常。rollbackFor属性用于控制出现何种异常类型,回滚事务。
例 :
1 2 3 4 5 6 7 8 9 @Transactional(rollbackFor = Exception.class) @Override public void deleteById(Integer id) throws Exception{ deptMapper.deleteById(id); if(true){ throw new Exception("出现异常"); } empMapper.deleteByDeptId(id); }
propagation 事务传播行为 :指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
REQUIRED:大部分情况下都是用该传播行为即可。
REQUIRED_NEW:当我们不希望事务之间相互影响时,可使用该传播行为。
AOP AOP :Aspect Oriented Programming(面向切面编程,面向方面编程),其实就是面向特定方法编程。
步骤
在pom.xml中导入AOP的依赖
1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
编写AOP程序:针对特定方法根据业务需要进行编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Slf4j @Component @Aspect //AOP类 public class TimeAspect { @Around("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))") //切入点表达式 public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable { //1. 记录开始时间 long begin = System.currentTimeMillis(); //2. 调用原始方法运行 Object result = joinPoint.proceed(); //3. 记录结束时间, 计算方法执行耗时 long end = System.currentTimeMillis(); log.info(joinPoint.getSignature()+"方法执行耗时: {}ms", end-begin); return result; } }
核心概念 连接点 :JointPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
通知 :Advice,指那些重复的逻辑,也就是共性功能(最终体现为一个方法)
切入点 :PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
切面 :Apect,描述通知与切入点的对应关系(通知+切入点)
目标对象 :Target,通知所应用的对象
执行流程 进行AOP程序的开发,运行的不再是原始目标对象,而是基于目标对象所生成的代理对象。
通知类型
@Around:环绕通知。此注解标注的通知方法在目标方法前后都被执行
@Before:前置通知,此注解标注的通知方法在目标方法前被执行
@After:后置通知,此注解标注的通知方法在目标方法后被执行
@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行
注 :
@Around环绕通知需要自己调用proceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行
@Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值
例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.itheima.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Slf4j @Component @Aspect public class MyAspect1 { @Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))") public void pt(){} @Before("pt()") public void before(){ log.info("before ..."); } @Around("pt()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { log.info("around before ..."); Object result = proceedingJoinPoint.proceed(); //调用目标对象的原始方法执行 log.info("around after ..."); return result; } @After("pt()") public void after(){ log.info("after ..."); } @AfterReturning("pt()") public void afterReturning(){ log.info("afterReturning ..."); } @AfterThrowing("pt()") public void afterThrowing(){ log.info("afterThrowing ..."); } }
通知顺序
不同切面类中,默认按照切面类的类名字母排序
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的后执行
用@Order(数字)加在切面类上来控制顺序
目标方法前的通知方法:数字小的先执行
目标方法后的通知方法:数字小的后执行
切入点表达式 描述切入点方法的一种表达式,即切入点表达式 。
作用 :主要用来决定项目中的哪些需要加入通知
常见形式 :
execution(…):根据方法的签名来匹配
@annotation(….):根据注释匹配
execution 语法 :
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带?的表示可以省略的部分
通配符
*: 单个独立的任意符号,可以通配任意返回值,报名,类名,方法名,任意类型的一个参数,也可以通配包,类,方法名的一部分
..:多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数
书写规范 :
所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配,如:查询类方法都是find开头,更新类方法都是update开头
描述切入点方法通常基于接口描述,而不是直接描述实现类,增强扩展性
在满足业务需要的前提下,尽量缩小切入点的匹配范围,例如:报名匹配尽量不使用..,使用*匹配单个包
@annotation @annotation切入点表达式,用于匹配标识有特定注解的方法。
例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 package com.itheima.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Mylog { } package com.itheima.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Slf4j @Component @Aspect public class MyAspect1 { @Pointcut("@annotation(com.itheima.aop.Mylog)") public void pt(){} @Before("pt()") public void before(){ log.info("aspect1 ..."); } } package com.itheima.service.impl; import com.itheima.aop.Mylog; import com.itheima.mapper.DeptMapper; import com.itheima.pojo.Dept; import com.itheima.service.DeptService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; @Slf4j @Service public class DeptServiceImpl implements DeptService { @Autowired private DeptMapper deptMapper; @Mylog @Override public List<Dept> list() { List<Dept> deptList = deptMapper.list(); return deptList; } @Mylog @Override public void delete(Integer id) { //1. 删除部门 deptMapper.delete(id); } @Override public void save(Dept dept) { dept.setCreateTime(LocalDateTime.now()); dept.setUpdateTime(LocalDateTime.now()); deptMapper.save(dept); } @Override public Dept getById(Integer id) { return deptMapper.getById(id); } @Override public void update(Dept dept) { dept.setUpdateTime(LocalDateTime.now()); deptMapper.update(dept); } }
连接点 在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,例如目标类名,方法名,方法参数等。
@Around:获取连接点信息只能使用ProceedingJoinPoint
对于其他四种通知,获取连接点信息只能用JoinPoint,它是ProceedingJoinPoint的父类型
例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.ljsblog.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.Arrays; @Slf4j @Component @Aspect public class MyAspect1 { @Pointcut("@annotation(com.itheima.aop.Mylog)") public void pt(){} @Around("pt()") public Object round(ProceedingJoinPoint joinPoint) throws Throwable { String name = joinPoint.getTarget().getClass().getName(); log.info("目标对象的类名{}",name); String methodName = joinPoint.getSignature().getName(); log.info("目标对象的方法名{}",methodName); Object[] args = joinPoint.getArgs(); log.info("目标对象的参数{}", Arrays.toString(args)); //放行目标方法执行 Object result = joinPoint.proceed(); log.info("目标方法的返回值为:{}",result); return result; } } //非Around注解中,方法参数用JoinPoint,获取目标对象各项属性的方法与ProceedingJoinPoint一致·