文章目录
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/becd13ab66a136ecd5e21064f60b4314.png)
事务管理
事务回顾
先来完善一下我们之前删除部门的操作
加一个根据部门ID删除对应员工的操作(因为你部门没了都,部门底下怎么还能有员工)
在对应mapper里写一下
就能实现
但是如果这样
中间出现异常的话就只能删除部门而删除不了对应部门的员工
正常我们要在方法头开启事务
最后回滚事务/提交事务
但在spring框架中
事务已经封装好了
到一个Transactional
spring事务管理
作用方法上,就是该方法交给Spring进行事务管理
作用在类/接口上就是对应类/接口所有的方法都交给Sping进行事务管理
我们只需要在一些执行多次数据访问操作的方法上写上对应注解
方法执行前开始事务,有异常回滚事务,无异常就提交事务
在springboot的yml配置文件配置个事务日志开关,方便观察(自己开发可以不用这一步)
然后再进行对应的操作
就会进行对应事务回滚
事务进阶
rollbackFor
就是默认只有RunTimeExpection才会回滚异常
如果你想包含全部需要指定rollbackFor属性
这样的话就不会进行事务回滚
propagation
就是你a方法开启事务,同时里面调用b方法
b方法也开启事务
那么b方法是新建事务还是加入到a方法的事务呢?
两种是有区别的,加入到a方法的事务,b中出错a一起回滚,而b新建事务,a中出错,不会影响b事务(前提b进行运行),但b出错a也会回滚,因为a调用了b(除非b自己捕获)
就涉及到事务传播行为
对应属性为Propagation
只需关注前两个即可
案例
delete调用的insert方法两都用spring事务管理
这时你请求删除部门
会报错,按理来说我们想要的是,把这个就算没删除的也弄到记录表中
但是你会发现其实insert并没有执行(数据库没有添加对应数据)
解析:
因为我们用的默认值
insert方法会参加对应delete的事务
delete事务出错,insert和delete在一个事务,都进行回滚
当然就添加不成功
如果把传播行为改成新建
a事务最后会调用b方法
但是b会自己开一个事务进行insert
里面没有报错b不会回滚
而a会回滚,这就是两种方式的区别
对应应用场景
AOP
比如你要统计时间
如果往每个service加上对应的起始时间,截止时间
会很麻烦,而且你不能在service里面只使用一个方法,因为计算的就是service的执行时长
所以用AOP来做,使先执行A方法的一段逻辑,然后再执行service,再执行A方法剩余的逻辑
就是给方法来增添或者修改功能
底层是用动态管理
其实AOP的应用很多
比如之前的事务管理其实底层就是AOP实现的
方法调用前开启事务,调用后结束/回滚事务
AOP快速入门
先导入依赖
然后定义一个类
用@Aspect修饰表示该类为AOP类
下面可以有多个方法,方法上用一个
注解@Around("execution(* com.itheima.service.*.*(..))")
属于通知类型的一种,决定我们这个方法是在目标方法前运行还是后运行
还是前后都有(后面有详解)
切入点表达式,后面后详细讲解,表示service下全部方法都要先经这个AOP的方法
来表示哪个包下的类的方法运行会先到在这个AOP类中的方法执行
参数joinPoint封装了原始方法的相关信息
这样就做到了没有修改其他业务层方法来实现一个统一的功能的操作
AOP核心概念
连接点JoinPoint,就是AOP控制的方法
通知也就是代码逻辑重复的部分,也就是我们想插入到普通方法的部分
切入点就是选择哪些方法是要指向这个通知的
切入点+通知=切面
目标对象,也就是切入点选定的那些方法
执行流程
它是建立了一个代理对象
然后通过切面类中的通知和目标对象中的方法进行构建
然后把代理对象给了IOC容器管进行DI注入
通知类型
用对应的注解
决定了AOP类的方法是在目标方法前运行还是后运行
还是在前后都执行
还有两个角色与异常相关的
注意环绕通知需要指定目标方法在哪执行!
需要执行ProceedingJoinPoint.proceed()让原方法执行,其他通知不需要考虑目标方法执行
因为其他都是指定了执行的顺序
演示一下
但是这样我们的切入点表达式都一样,我们来简化一下
使用@PointCut注解提取对应的切入点表达式
通知执行顺序
先说不同注释
Around和Before和After三个之间
先执行Around的before就是前半段
再执行Before再执行对应的方法
再执行afterReturning(因为这个是和内部方法关联的)内部方法正常执行完返回就会运行这个
如果是异常的话,就执行对应After Throwing
再执行After注解
最后执行Around after
就是Around把整个方法和before注解和after注解全包括了,所以他是Around
再说相同注解的
和我们AOP类的类名有关
目标方法前的通知方法,字母排名靠前的先执行
目标方法后的通知得到,字母排名靠前的反而后执行
也可以用@Order(数字)指定在AOP类上,数字代表执行顺序
切入点表达式
切入点表达式
处理我们之前用到的execution
还有annotation
@execution
对应语法
execution(访问修饰符 返回值 包名.类名.方法名(方法参数) throw 异常)
访问修饰符和包名.类名可以省略
然后对应抛出的异常也可以省略
对应通配符
比如这个第一个
就是返回值、第二级包、类/接口、一个***(因为对应匹配单个独立的任意符号)**任意的参数
==update代表方法以update开头后面什么都可以匹配(后面没有也可以)(好像除了参数但是单个符号,就是一级包名也算单个独立符号)==
…这个就主要出现在包和参数里面,匹配的是多个任意的符号
execution常见的切入点表达式
最后一级是类/接口
app就是任意开头以app为结尾的
一般我们最后指定接口方便对应的扩展(就算添加新类只要实现接口,也能被对应的方法通知到)
建议精准匹配
@annotation
用上面那种匹配两个类型的方法比较麻烦
而@annotation恰好能解决这个问题
这个就不是指定方法了
是指定对于的注解(注解类),只要是被对应注解类标识的方法,都会被通知
1.首先定义一个注解(在方法上,运行时生效)
2.在你想要通知的方法上加上我们自定义的注解
3.再通过切面类指定我们定义的注解
这样对应被注解表示的方法回受到通知
小结
方法名规则用execution
方法名不规则或者想通知多个,可以用annotation
连接点(AOP控制的方法)
Around对应ProceedingJoinPoint
常用方法
getTarget().getClass().getName()获取到目标类名
getSignature()获取目标方法签名
getSignature().getName()获取目标方法名
getArgs()获取密码方法运行参数
proceed()方法,执行原始方法获取返回值
其他通知只能用JoinPoint,做参数来调用
这个父类没有proceed其他都一样,因为他已经运行这四种通知类型才能通知
案例记录操作日志
就是再所有方法上对应加上通知呗
然后保存在数据库表中,方便对数据进行追踪
稍微分析一下
步骤
操作日志表
和表对应的实体类
然后我们在mapper里面弄一个方法来进行操作日志的添加操作
定义注解
定义切面类最主要是怎么获取对应的信息
操作人ID从httpservletRequest里面获取JWT请求头的ID
还要注入一个请求类,获取当前请求
其他的参数好说一点,然后封装到我们的对象中即可