day14SpringBootWeb事务&AOP 8.7
- Spring事务管理:
@Transactionl (service层的方法上、类上、接口上)(增删改)
作用:成功自动提交,失败自动回滚
配置文件中写(固定的)
logging: level: org.springframework.jdbc.support.JdbcTransactionManager: debug |
- 事务进阶
@Transactionl(rollerbackFor=Exception.class)//加了,所有异常都回滚
(默认只有出现 RuntimeException(运行异常) 才回滚异常。而如果出现编译时异常,则不回滚。)
Propagation:(事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。)
@Transactional(propagation=Propagation.属性值)
属性值 | 含义 | 说明 |
REQUIRE | 【默认值】需要事务,有则加入,无则创建新事务 | 大部分情况下都是用该传播行为即可 |
REQUIRE_NEW | 需要新事务,无论有无,总是创建新事务 | 在被调用的方法上添加(如下订单,不论成功与否,都要保证日志成功记录) |
SUPPORTS | 支持事务,有则加入,无则在独立的连接中运行 SQL | 结合 Hibernate、JPA 时有用,配在查询方法上 |
NOT_SUPPORTED | 不支持事务,不加入,在独立的连接中运行 SQL | |
MANDATORY | 必须有事务,否则抛异常 | |
NEVER | 必须有事务,否则抛异常 | |
NESTED | 嵌套事务 | 仅对DataSourceTransactionManager有效 |
AOP基础:Aspect Oriented Programming(面向切面编程),将重复的逻辑剥离出来,在不修改原始逻辑的基础上对原始功能进行增强或改变。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 2.定义类抽取公共代码,标识当前类是一个AOP类,并被Spring容器管理,配置公共代码作用于哪些目标方法(切面类) @Component //被IOC容器管理 @Aspect //标识为AOP类 @Slf4j //日志 public class TimeAspect { @Around("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")//切入点表达式,所作用的位置,改位置的方法每一次执行,此AOP文件也执行 public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//运行joinPoint.proceed()方法可能有异常所以抛 long begin = System.currentTimeMillis();//记录方法运行开始的时间 Object result = joinPoint.proceed(); //调用原始操作 long end = System.currentTimeMillis();//记录方法运行结束的时间 log.info("执行耗时 : {} ms", (end-begin));//日志信息显示: return result; } } |
执行流程:
当目标对象(ServiceImpl实现类)功能需要被增强时,并且我们使用AOP方式定义了增强逻辑(在Aspect类中)
Spring会为目标对象自动生成一个代理对象,并在代理对象对应方法中,结合我们定义的AOP增强逻辑完成功能增强
AOP核心概念:
● 连接点:JoinPoint,可以被AOP控制的方法执行(包含方法信息) ● 通知:Advice ,重复逻辑代码 ● 切入点:PointCut ,匹配连接点的条件 ● 切面:Aspect,通知+切点 ● 目标对象:Target,通知所应用的对象 |
通知类型
@Around: | 此注解标注的通知方法在目标方法前、后都被执行 |
@Before: | 此注解标注的通知方法在目标方法前被执行 |
@After : | 此注解标注的通知方法在目标方法后被执行,无论是否有异常 |
此注解标注的通知方法在目标方法后被执行,有异常不会执行 | |
此注解标注的通知方法发生异常后执行 |
可将切入点表达式保证成一个方法(若设成公共的,其他类也可以使用)
@Pointcut(“execution(com.各级包名....ServiceImpl.*(..))”) Public void 方法名(){} |
在需要的AOP方法上写 :@通知类型(“方法名()”) |
通知执行顺序:默认按照切面类的名称字母排序
在AOP类上加注解:@Order(数字)排序(数字小的先执行)
切点表达式:execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配
execution(访问修饰符? 返回值 包名.类名?.方法名(方法参数) throws 异常?) | |
访问修饰符: 包名.类名: throws 异常: | 可省略(没啥用,仅能匹配 public、protected、包级,private 不能增强) 可省略 可省略(注意是方法上声明抛出的异常,不是实际抛出的异常) |
* :单个独立的任意符合,可以统配任意返回值,包名,类名,方法名,任意类型的一个参数,也可以统配包,类,方法名的一部分 execution( *com.*.service.*.updat*(*)) | |
.. :多个连续的任意符号,可以统配任意层级的包,或任意类型,任意个数的参数 Execution(*com.itheima..DeptService.*(..)) | |
根据业务需要,可以使用 && || ! 来组合比较复杂的切入点表示式 | |
包名和类名不建议省略 @Pointcut(“executino(* *(..))”) 最统配的写法 |
@annotation (注解全类名)
1.(创建一个Annotationl类,注解名自定义) 2.在创建的注解上加@Retention(RetentionPolicay.RUTIME)//什么时间生效:运行时 @Target(ElementType.METHOD) //生效的位置:方法上 |
3.在需要的方法名上加 @创建的注解名 4.改造切点表达式:Pointcut(@annotation(com.itheima.aop.创建的注解名)) //(注解的位置) |
链接点:是目标方法,在Spring 中用 JoinPoint 抽象了连接点,用它可以获得方法执行时的相关信息,如方法名、方法参数类型、方法实际参数等等
对于 @Around 通知,获取连接点信息只能使用 ProceedingJoinPoint 对于其他四种通知,获取连接点信息只能使用 JoinPoint,是 ProceedingJoinPoint 的父类型 |
public void 方法名(JoinPoint joinPoint){ //获取目标对象的类名 String className =joinPoint.getTarget().getClass().getName(); //获取目标方法名 String methodName =joinPoint.getSignature().getName(); //获取目标方法运算时传入的参数,是一个数数组,输出时有Arrays.toString(args) //一种输出写法:Arrays.asList(joinPoint.getArgs()).toString() Object[] args =joinPoint.getArgs(); //获取返回值 Object result=joinPoint.proceed();//调用方法运行 //在日志中显示 Log.info(“内容”) }} |
实例: 1.在数据库中创建一个表,来存放日志信息 和对应信息的pojo类
|
around先执行原始方法前的代码,在运行原始代码,在运行原始代码后的代码 获取当前登录用户:获取request对象,从请求头中获取jwt令牌,解析令牌获取出当前用户的id |