欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > JavaWeb学习——事务管理、AOP学习

JavaWeb学习——事务管理、AOP学习

2024/10/24 19:17:30 来源:https://blog.csdn.net/ping12138/article/details/141506061  浏览:    关键词:JavaWeb学习——事务管理、AOP学习

目录

一、事务管理

1、事务回顾

 2、事务进阶

a、rollbackFor属性

b、propagation属性

二、AOP学习

1、基础了解

2、AOP进阶


一、事务管理

1、事务回顾

        事务的概念:事务必须服从ACID原则。ACID指的是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。通俗理解,事务其实就是一系列指令的集合。

        事务的特性

        原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
        一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
        隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。
        持久性:当事务正确完成后,它对于数据的改变是永久性的。

下面提供一个事务操作发生的图进行述说:

        感想:其实联系我之前学习的MySql知识,可以发现此事务就是当时学习到的事务概念,主要的是对数据库操作进行一个集合类的统合,实现数据的同步修改性。

接下来举一个案例来说明使用事务的原因

        分析:上图是一个对部门表实现删除部门的代码案例,首先它能实现基础的连带部门信息和部门人员信息的删除,但当代码执行过程中出现异常,如上图设置的1/0,就会中断代码执行,在前端表现就是只删除了部门信息,数据库里也是如此,它们不能一致的发生删除或不删除,由此我们就要利用到事务来进行管理,实现同步。

使用注释,进行事务集合使用(@Transactional)

        分析:由图可知该注释可以使用在方法、类、接口上,自由度很高,而要解决上面提到的问题,只要在方法上实现就行。

补充:spring事务管理日志依赖添加

        添加原因:Spring 事务管理是 Spring 框架中一个重要的模块,它提供了强大的事务管理能力,使得我们可以对事务操作进行细粒度的控制。

        添加代码

#spring事务管理日志
logging:level:org.springframework.jdbc.support.JdbcTransactionManager: debug

 2、事务进阶

a、rollbackFor属性

        作用:在 Spring 数据访问操作中,默认情况下只有未检查(unchecked)异常(继承自 RuntimeException 的异常)或者 Error 会导致事务回滚,而受检查的异常(即非 RuntimeException 异常)则不会导致事务回滚。rollbackFor 属性就是用来解决这个问题的,它可以指定受检查的异常也可以导致事务回滚。你可以设置任何你希望导致此行为的异常类型。

代码示例

 @Transactional(rollbackFor = Exception.class) //spring事务管理//@Transactional@Overridepublic void delete(Integer id) throws Exception {try {deptMapper.deleteById(id); //根据ID删除部门数据int i = 1/0;//if(true){throw new Exception("出错啦...");}empMapper.deleteByDeptId(id); //根据部门ID删除该部门下的员工} finally {DeptLog deptLog = new DeptLog();deptLog.setCreateTime(LocalDateTime.now());deptLog.setDescription("执行了解散部门的操作,此次解散的是"+id+"号部门");deptLogService.insert(deptLog);}}

        分析:上图代码的效果仍是删除部门相关信息,只是最后的完成版,在这我们不用太在意另外的代码,只用看@Transactional后面括号里的rollbackFor使用,这里是指定所有的异常,我们也可以换另外的指定异常类型。

b、propagation属性

        作用propagation 是 Spring 的 @Transactional 注解的一个属性,它用于指定事务的传播行为。事务的传播行为决定了在方法被调用时,事务是如何沿着调用链传播的。

使用类型

  1. REQUIRED(Propagation.REQUIRED):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认的设置。

  2. SUPPORTS(Propagation.SUPPORTS):如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  3. MANDATORY(Propagation.MANDATORY):如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

  4. REQUIRES_NEW(Propagation.REQUIRES_NEW):创建一个新的事务,如果当前存在事务,则把当前事务挂起。

  5. NOT_SUPPORTED(Propagation.NOT_SUPPORTED):以非事务方式运行操作,如果当前存在事务,就把当前事务挂起。

  6. NEVER(Propagation.NEVER):以非事务方式运行,如果当前存在事务,则抛出异常。

  7. NESTED(Propagation.NESTED):如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED

使用视图

 

        感想:这一属性的使用就是可以自由设置当多重事务关联触发时,我们来决定它们的运行顺序和存在周期。

二、AOP学习

1、基础了解

AOP的概述

        AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,旨在促进更高效、组织性更好的代码。这种模式主要聚焦于程序逻辑的横向关注点,也就是那些几乎出现在所有层级代码中的公共功能(例如日志、事务管理等),而这种功能在没有 AOP 的情况下,往往需要重复编写,这会导致代码冗余。

以下是 AOP 的主要概念

  1. Aspect(切面):一个模块,具有一组提供公共功能的 APIs。在 AOP 中,切面用于封装每个横切关注点的逻辑,这些逻辑可以在运行时动态地应用到不同的对象和函数上。

  2. Join Point(连接点):程序执行过程中的某个特定点,如方法被调用时、异常被抛出时等。在 Spring AOP 中,一个连接点总是代表一个方法的执行。

  3. Advice(通知):是切面必须完成的工作,也就是切面的具体行为。根据其所在位置,通知的类型有 before、after、around、afterThrowing、afterReturning 等。

  4. Pointcut(切入点):匹配连接点的规则或概述。在 Spring AOP 中,切入点可以使用 AspectJ 的切入点表达式语言来指定。

  5. Target Object(目标对象):被一个或多个切面所通知的对象。

  6. Weaving(织入):将切面和其它应用类型或者对象连接起来,创建一个被通知的对象。这可以在编译时(使用 AspectJ 编译器),类加载时,或在运行时完成。

        Spring AOP 框架将这些概念与 Spring 的 IoC(Inversion of Control,控制反转)容器和 Spring 提供的其它功能无缝集成,从而提供了一个完整、易用,而且与 Java 语言和 Spring 框架高度集成的 AOP 实现。

应用场景

        AOP(面向切面编程)在许多编程场景中都能发挥重要作用,主要因为它有助于代码的解耦和重复代码的减少。以下是一些 AOP 常见的应用场景:

  1. 日志记录:通过把日志记录的功能放到切面中处理,可以简化主要业务逻辑代码的清晰度,并且可以非常方便地控制日志记录的粒度和范围。

  2. 事务管理:在许多企业级应用中,事务管理是一项关键任务。AOP 可以简化事务操作,通过预定义的策略,使开发者可以透明地管理和配置事务。

  3. 性能监控:AOP 可以用于收集方法调用的统计信息,例如:方法调用的次数以及每次调用的持续时间等。这些信息可以用于后续的性能分析和优化。

  4. 安全检查:可以用AOP实现统一的安全检查,避免在每个需要进行安全检查的方法中单独实现。

  5. 错误处理:可以通过 AOP 对所有方法进行统一的错误捕获和处理,减少重复的错误处理代码。

  6. 缓存:可以通过 AOP 实现方法结果的缓存,提高系统的性能。

  7. 验证和格式化:AOP 可以用于验证对象的状态或格式化对象的输出,将这些横切关注点从主要任务中分离出来。

以上是 AOP 最常见的应用场景,但是并不受此限。

Spring AOP与动态代理

Spring AOP(面向切面编程)在它的底层实现上主要使用了动态代理。

        动态代理是一种设计模式,主要是用于接口的非入侵式代理,不改变类文件和原有业务逻辑的情况下,增加额外的功能。Java的java.lang.reflect包中提供的Proxy类和InvocationHandler接口,就是用于生成动态代理类和处理代理方法调用的。

Spring AOP 使用了两种类型的动态代理:

1. **JDK动态代理**:基于接口的动态代理,JDK动态代理通过实现InvocationHandler接口,并重写invoke方法来处理代理逻辑。如果目标对象有接口,Spring AOP会选择使用JDK动态代理。

2. **CGLIB代理**:基于类的动态代理,CGLIB通过生成目标类的子类来处理代理逻辑。如果目标对象没有实现任何接口,Spring AOP会选择使用CGLIB代理。

在Spring AOP中,切面的逻辑(例如事务管理、日志记录等)通常会写在Advice中,然后这些通知会被动态织入到你指定的方法上,这样在调用这些方法时,就会执行对应的通知逻辑。

        总的来说,动态代理构成了Spring AOP的核心,Spring通过动态代理技术实现了其强大的面向切面编程功能。

举例说明(统计各个业务层方法执行耗时)

在实现AOP的使用前,我们要加入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 编写的AOP使用案例代码(实现统计执行耗时)

package com.sunny.aop;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.sunny.service.impl.DeptServiceImpl.*(..))") //切入点表达式//@Around("com.sunny.aop.MyAspect1.pt()")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;}}

AOP的主要概念联系

这里提到的主要概念就是上面的主要概念,接下来举一个例子来进行说明。

以下是这些概念在一次典型的AOP操作中如何协同工作的示例:

  1. 定义一个Aspect,例如日志切面,将日志记录通知(Advice)与切入点(Pointcut,例如全部的服务操作方法)关联起来。

  2. 当程序执行到切入点的方法时,即触发了连接点(Join point),例如调用服务操作。

  3. 由于切面的存在,切入点的方法会被切面中的通知Advice捕获,这时候,日志切面的日志记录代码就在目标方法执行之前或之后执行,这个过程就是织入(Weaving)。

        总结起来,AOP的核心概念关联使用,可以使系统的服务模块关注于核心业务,而像日志,事务,安全等服务可以通过AOP的方式进行模块化和集中管理,提高了系统的可维护性和可重用性。

补充:Advice的使用类型

有以下几种类型的advice:

  1. Before advice:在切入点选择的连接点之前执行的通知(比如,一个特定的方法调用之前)。
  2. After returning advice:在切入点选择的连接点成功完成后,例如,方法成功返回结果后,执行的通知。
  3. After throwing advice:在方法抛出异常退出时执行的通知。
  4. After (finally) advice:当切入点结束后执行的通知,无论方法退出是正常还是异常返回。
  5. Around advice:包围着被通知的方法,可以在被通知的方法前后自定义行为。

        这些advice中,我们自定义的代码就是那些横切关注点的实现, 例如在方法调用前记录日志(Before advice),在方法异常时发送通知(After throwing advice)等。

下面给些代码案例

package com.sunny.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.sunny.service.impl.DeptServiceImpl.*(..))")//使用切入,指定表达式public void pt(){}@Before("pt()")//Advice使用public void before(){log.info("before ...");}@Around("pt()")//Advice使用public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {log.info("around before ...");//调用目标对象的原始方法执行Object result = proceedingJoinPoint.proceed();log.info("around after ...");return result;}@After("pt()")//Advice使用public void after(){log.info("after ...");}@AfterReturning("pt()")//Advice使用public void afterReturning(){log.info("afterReturning ...");}@AfterThrowing("pt()")//Advice使用public void afterThrowing(){log.info("afterThrowing ...");}
}

2、AOP进阶

@Around的使用补充

@Pointcut的使用图示

多个切点的通知顺序分析(多个切面的切入点匹配到了相同的目标方法)

切入点表达式学习

常见形式

execution详细使用

代码示例

@Pointcut("execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")@Pointcut("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")@Pointcut("execution(void delete(java.lang.Integer))") //包名.类名不建议省略@Pointcut("execution(void com.itheima.service.DeptService.delete(java.lang.Integer))")@Pointcut("execution(void com.itheima.service.DeptService.*(java.lang.Integer))")@Pointcut("execution(* com.*.service.DeptService.*(*))")@Pointcut("execution(* com.itheima.service.*Service.delete*(*))")@Pointcut("execution(* com.itheima.service.DeptService.*(..))")@Pointcut("execution(* com..DeptService.*(..))")@Pointcut("execution(* com..*.*(..))")@Pointcut("execution(* *(..))") //慎用

annotation详细使用

代码示例

package com.sunny.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 创建名为MyLog的切入点*/
@Retention(RetentionPolicy.RUNTIME) //描述注解什么时候使用到
@Target(ElementType.METHOD) //描述注解的使用对象——方法
public @interface MyLog {
}@MyLog //加入设定的annotation注解@Overridepublic List<Dept> list() {List<Dept> deptList = deptMapper.list();return deptList;}@MyLog //加入设定的annotation注解@Overridepublic void delete(Integer id) {//1. 删除部门deptMapper.delete(id);}

连接点使用

代码案例

package com.sunny.aop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.util.Arrays;//切面类
@Slf4j
@Aspect
@Component
public class MyAspect8 {@Pointcut("execution(* com.sunny.service.DeptService.*(..))")private void pt(){}@Before("pt()")public void before(JoinPoint joinPoint){log.info("MyAspect8 ... before ...");}@Around("pt()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("MyAspect8 around before ...");//1. 获取 目标对象的类名 .String className = joinPoint.getTarget().getClass().getName();log.info("目标对象的类名:{}", className);//2. 获取 目标方法的方法名 .String methodName = joinPoint.getSignature().getName();log.info("目标方法的方法名: {}",methodName);//3. 获取 目标方法运行时传入的参数 .Object[] args = joinPoint.getArgs();log.info("目标方法运行时传入的参数: {}", Arrays.toString(args));//4. 放行 目标方法执行 .Object result = joinPoint.proceed();//5. 获取 目标方法运行的返回值 .log.info("目标方法运行的返回值: {}",result);log.info("MyAspect8 around after ...");return result;}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com