AOP&事务管理-待续

关于spring的事务管理
比如删除某个部门的数据,紧接着部门下的员工数据也应当一并删除
解散部门: 删除部门,同时删除该部门下的员工
假如删除部门的过程中发生异常,就会删除员工失败就造成了数据的不一致

注解:@Transactional
位置:业务(service)层的方法上、类上、接口上
作用:将当前方法交给spring进行事务管理,方法执行前,开启事务,成功执行完毕,提交事务,出现异常,回滚事务
一般注解写在业务层的增删改的方法上
一般查询数据的操作不对数据的变更所以无需操作事务
事务的日志开关配置

#开启事务管理日志
logging:
  level:
     org.springframework.jdbc,support,jdbcTransactionManager: debug

事务进阶
rollbackFor
propagation

rollbackFor事务回滚
默认情况下,只有出现 RuntimeException 才回滚异常。rollbackFor属性用于控制出现何种异常类型,回滚事务。
这里的意思是必须是运行时遇到的异常比如i= 1/ 0而不是抛出的异常,所以抛出的不会被事务回滚
所以必须是RuntimeException
@Transactional(rollbackFor = Exception.class) 后面这个意味着异常的类型
这样就代表着所有的异常都会回滚

propagation事务属性-传播行为
事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
主要用到就下面这两个
REQUIRED默认值 : 有则加入,无则新创建事务
REQUIRES_NEW :无论有无,都新创建事务

需求:解散部门时,无论是成功还是失败,都要记录操作日志。
步骤:
1解散部门:删除部门、删除部门下的员工
2记录日志到数据库表中

记录日志
准备一个表dept_log三个字段id,createtime、描述
直接在删除员工的方法无论成功与否都加上
所以前面的删除操作放在try里
而后的日志操作放在finally里

DeptLog deptLog = new DeptLog();
deptLog.setCreateTime(LocalDateTime.now());
deptLog.setDescription("执行了解散部门的操作,此次解散的是"+id+"号部门");
deptLogService.insert(deptLog);

关于deptLogService的insert方法
来操作插入日志

解释下
这个删除操作是一个事务、日志操作紧跟删除也是一个事务,然而删除的异常影响日志的添加。所以避免默认日志也是加入删除事务的情况。就要修改propagation
修改事务的传播行为,应当是REQUIRES_NEW创建一个新事物,所以就不受前者事务的影响了

@Service
public class DeptLogServiceImpl implements DeptLogService{
    @Autowired
    private DeptLogMapper deptLogMapper;

    @Transactional(propagation = Propagation.REQUIRES NEW)
    @Override
    public void insert(DeptLog deptLog){
        deptLogMapper.insert(deptLog);
  }
}

grep console插件用于日志的过滤、也就是字体背景的染色

AOP概述
AOP: (面向切面编程、面向方面编程),其实就是面向特定方法编程

导入依赖:在pom.xml中导入AOP的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

1.获取方法运行的开始时间
2.运行原始方法
3.获取方法运行结束时间,计算执行耗时

@Aspect代表当前不是一个普通类而是一个AOP类
对记录时间方法上的注解@Around()
表达式的方式指定哪些特定的方法进行编程.代表类名或接口名 .代表方法名

@slf4j
@Component
@Aspect //AOP类
public class TimeAspect {
  
    @Around("execution(* com.itheima,service.*.*(..))")      //切入点表达式
    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);    //getsignature拿到原始方法的签名,也就是知道了哪些方法的执行耗时  

        return result;
  }

com.itheima.service.impl.DeptServiceImpl.list()目标对象
1.AOP核心概念
连接点 (JoinPoint)切入点(PointCut)通知 (Advice)切面 (Aspect)目标对象(Target)
2.AOP执行流程

AOP进阶
通知类型:Before、After、Around
通知顺序
切入点表达式
连接点
特殊的环绕通知,需要返回值为Object拿出去,原始方法执行
@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
@Around环绕通知方法的返回值,必须指定为object,来接收原始方法的返回值。

对这个大量的切入点表达式进行抽取

//引用切入点表达式
@Pointcut("execution(* com,itheima.service.impl.DeptServiceImpl.*(..))")
private void pt(){}

@Before("pt ()")
public void b……(){}
//如果需要在其他切面类中引用,需要将这个pt方法改成public,直接com.ithm.aop.……pt()

执行顺序
不同切面类中,默认按照切面类的类名字母排序
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的后执行
before基本是自然顺序,而after是自然顺序的倒叙
@Order(1)可以排序,数字越小,原始方法越先执行

切入点表达式

  1. execution(......:根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配
    execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
@Pointcut("execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")    //包名 类名不建议省略
private void pt(){

  }

也直接指定接口,接口来描述切入点

@Pointcut("execution(void com.itheima.service.DeptService.delete(java.lang.Integer))")
private void pt (){}

通配符描述切入点

*单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
..多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

  1. @annotation(......) :根据注解匹配 标识有特定注解的切入点的
    @Before("@annotation(com.itheima.anno.Log)")
    public void before(){
    log.info("before ....");
    }

关于自定义一个注解。创建一个标识注解,创建annotation名字Mylog
@Retention描述注解什么时候生效 RUNTIME直接运行时生效
@Target(METHOD)当前注解可以作用在哪些地方

在创建切入点@Pointut的时候("@annotation(com.hm.aop.mylog)")
然而哪个方法上面加上相应的注解标记mylog就从哪些方法上切入匹配
通过指定的注解的方式匹配切入点

AOP进阶-连接点(可以被AOP控制的方法)

待续……

posted @ 2024-01-29 19:25  launch  阅读(3)  评论(0编辑  收藏  举报