本文主要是介绍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
注解的一个属性,它用于指定事务的传播行为。事务的传播行为决定了在方法被调用时,事务是如何沿着调用链传播的。
使用类型:
-
REQUIRED(Propagation.REQUIRED)
:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认的设置。 -
SUPPORTS(Propagation.SUPPORTS)
:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 -
MANDATORY(Propagation.MANDATORY)
:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 -
REQUIRES_NEW(Propagation.REQUIRES_NEW)
:创建一个新的事务,如果当前存在事务,则把当前事务挂起。 -
NOT_SUPPORTED(Propagation.NOT_SUPPORTED)
:以非事务方式运行操作,如果当前存在事务,就把当前事务挂起。 -
NEVER(Propagation.NEVER)
:以非事务方式运行,如果当前存在事务,则抛出异常。 -
NESTED(Propagation.NESTED)
:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED
。
使用视图:
感想:这一属性的使用就是可以自由设置当多重事务关联触发时,我们来决定它们的运行顺序和存在周期。
二、AOP学习
1、基础了解
AOP的概述
AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,旨在促进更高效、组织性更好的代码。这种模式主要聚焦于程序逻辑的横向关注点,也就是那些几乎出现在所有层级代码中的公共功能(例如日志、事务管理等),而这种功能在没有 AOP 的情况下,往往需要重复编写,这会导致代码冗余。
以下是 AOP 的主要概念:
-
Aspect(切面):一个模块,具有一组提供公共功能的 APIs。在 AOP 中,切面用于封装每个横切关注点的逻辑,这些逻辑可以在运行时动态地应用到不同的对象和函数上。
-
Join Point(连接点):程序执行过程中的某个特定点,如方法被调用时、异常被抛出时等。在 Spring AOP 中,一个连接点总是代表一个方法的执行。
-
Advice(通知):是切面必须完成的工作,也就是切面的具体行为。根据其所在位置,通知的类型有 before、after、around、afterThrowing、afterReturning 等。
-
Pointcut(切入点):匹配连接点的规则或概述。在 Spring AOP 中,切入点可以使用 AspectJ 的切入点表达式语言来指定。
-
Target Object(目标对象):被一个或多个切面所通知的对象。
-
Weaving(织入):将切面和其它应用类型或者对象连接起来,创建一个被通知的对象。这可以在编译时(使用 AspectJ 编译器),类加载时,或在运行时完成。
Spring AOP 框架将这些概念与 Spring 的 IoC(Inversion of Control,控制反转)容器和 Spring 提供的其它功能无缝集成,从而提供了一个完整、易用,而且与 Java 语言和 Spring 框架高度集成的 AOP 实现。
应用场景
AOP(面向切面编程)在许多编程场景中都能发挥重要作用,主要因为它有助于代码的解耦和重复代码的减少。以下是一些 AOP 常见的应用场景:
-
日志记录:通过把日志记录的功能放到切面中处理,可以简化主要业务逻辑代码的清晰度,并且可以非常方便地控制日志记录的粒度和范围。
-
事务管理:在许多企业级应用中,事务管理是一项关键任务。AOP 可以简化事务操作,通过预定义的策略,使开发者可以透明地管理和配置事务。
-
性能监控:AOP 可以用于收集方法调用的统计信息,例如:方法调用的次数以及每次调用的持续时间等。这些信息可以用于后续的性能分析和优化。
-
安全检查:可以用AOP实现统一的安全检查,避免在每个需要进行安全检查的方法中单独实现。
-
错误处理:可以通过 AOP 对所有方法进行统一的错误捕获和处理,减少重复的错误处理代码。
-
缓存:可以通过 AOP 实现方法结果的缓存,提高系统的性能。
-
验证和格式化: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操作中如何协同工作的示例:
-
定义一个Aspect,例如日志切面,将日志记录通知(Advice)与切入点(Pointcut,例如全部的服务操作方法)关联起来。
-
当程序执行到切入点的方法时,即触发了连接点(Join point),例如调用服务操作。
-
由于切面的存在,切入点的方法会被切面中的通知Advice捕获,这时候,日志切面的日志记录代码就在目标方法执行之前或之后执行,这个过程就是织入(Weaving)。
总结起来,AOP的核心概念关联使用,可以使系统的服务模块关注于核心业务,而像日志,事务,安全等服务可以通过AOP的方式进行模块化和集中管理,提高了系统的可维护性和可重用性。
补充:Advice的使用类型
有以下几种类型的advice:
- Before advice:在切入点选择的连接点之前执行的通知(比如,一个特定的方法调用之前)。
- After returning advice:在切入点选择的连接点成功完成后,例如,方法成功返回结果后,执行的通知。
- After throwing advice:在方法抛出异常退出时执行的通知。
- After (finally) advice:当切入点结束后执行的通知,无论方法退出是正常还是异常返回。
- 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;}
}
这篇关于JavaWeb学习——事务管理、AOP学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!