AOP事务管理的原理与及三种实现方式

您所在的位置:网站首页 aop拦截jar内的方法 AOP事务管理的原理与及三种实现方式

AOP事务管理的原理与及三种实现方式

2023-09-07 14:35| 来源: 网络整理| 查看: 265

AOP事务管理

文章目录 AOP事务管理AOP的定义AOP的基本概念个人愚解实现方式XML实现注解实现 org.aspectj坐标依赖引入添加spring.xml的配置注解实现类 建议推荐使用 spring-txmaven项目导入依赖xml配置注解实现

AOP的定义

spect Oriented Programing 面向切面编程,相比较 oop 面向对象编程来说,Aop关注的不再是程序代码中某个类,某些方法,而aop考虑的更多的是一种面到面的切入,即层与层之间的一种切入,所以称之为切面。联想大家吃的汉堡(中间夹肉)。那么aop是怎么做到拦截整个面的功能呢?考虑前面学到的servlet filter /* 的配置,实际上也是aop 的实现。

作用:

AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用。

特点:

降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)提高了代码的复用性。提高系统的扩展性。(高版本兼容低版本)可以在不影响原有的功能基础上添加新的功能 AOP的基本概念

Joinpoint(连接点)

被拦截到的每个点,spring中指被拦截到的每一个方法,spring aop一个连接点即代表一个方法的执行。

Pointcut(切入点)

对连接点进行拦截的定义(匹配规则定义规定拦截哪些方法,对哪些方法进行处理),spring 有专⻔的表达式语言定义。

Advice(通知)

拦截到每一个连接点即(每一个方法)后所要做的操作

前置通知(前置增强)— before() 执行方法前通知返回通知(返回增强)— afterReturn 方法正常结束返回后的通知异常抛出通知(异常抛出增强)— afetrThrow()最终通知 — after 无论方法是否发生异常,均会执行该通知。环绕通知 — around 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

Aspect(切面)

切入点与通知的结合,决定了切面的定义,切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么,切面则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切面则是横切关注点抽象。

Target(目标对象)

被代理的目标对象

Weave(织入)

将切面应用到目标对象并生成代理对象的这个过程即为织入

Introduction(引入)

在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入

个人愚解

在这里插入图片描述

切面应该在service层!!!

实现方式XML实现

内部事务:用xml实现(导入依赖中的事务)

手动事务:注解实现(自己写的,项目中的事务)

/** *切面 *切入点和通知的抽象(与面向对象中的类相似) *定义切入点和通知(切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么) */ @Component// 将对象交给IOC容器去实例化 public class LogCut02 { public void cut(){ } /** * 声明前置通知并将通知应用到定义的切入点上 * 目标类方法执行前执行该通知 * */ public void before() { System.out.println("前置通知....."); } /** * 声明返回通知并将通知应用到定义的切入点上 * 目标类方法(无异常)执行后执行该通知 * */ public void afterReturn() { System.out.println("返回通知....."); } /** * 声明最终通知并将通知应用到定义的切入点上 * 目标类方法(无异常或有异常)执行后执行该通知 * */ public void after() { System.out.println("最终通知....."); } /** * 声明异常通知并将通知应用到定义的切入点上 * 目标类方法出现异常时执行该通知 */ public void afterThrow(Exception e) { System.out.println("异常通知....."+" 异常原因:"+e.getCause()); } /** * 声明环绕通知并将通知应用到切入点上 * 方法执行前后通过环绕通知定义相应处理 * 需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();) * @param pjp * @return */ public Object around(ProceedingJoinPoint pjp) { System.out.println("前置通知..."); Object object=null; try {object=pjp.proceed(); System.out.println(pjp.getTarget() +"======"+pjp.getSignature()); // System.out.println("返回通知..."); } catch (Throwablethrowable) { throwable.printStackTrace(); System.out.println("异常通知..."); } System.out.println("最终通知..."); return object; }

beans.xml添加的配置

xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd .... 注解实现 org.aspectj 坐标依赖引入 org.aspectj aspectjweaver 1.8.9 添加spring.xml的配置

添加命名空间 配置AOP代理

xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd .... 注解实现类 /** * 切面* 切入点和通知的抽象(与面向对象中的类相似) * 定义切入点和通知(切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么) */ @Component// 将对象交给IOC容器去实例化 @Aspect// 声明当前类是一个切面 public class LogCut { /** * 切入点: * 匹配规则。规定什么方法被拦截、需要处理什么方法 * 定义切入点 * @Pointcut("匹配规则") * * Aop 切入点表达式简介 * 1. 执行任意公共方法: * execution(public *(..)) * 2. 执行任意的set方法* execution(* set*(..)) * 3. 执行com.xxxx.service包下任意类的任意方法 * execution(* com.xxxx.service.*.*(..)) * 4. 执行com.xxxx.service 包以及子包下任意类的任意方法 * execution(* com.xxxx.service..*.*(..)) * * 注:表达式中的第一个* 代表的是方法的修饰范围 * 可选值:private、protected、public (* 表示所有范围)*/ @Pointcut("execution (* com.xxxx.service..*.*(..) )") public void cut(){ } /** * 声明前置通知并将通知应用到定义的切入点上 * 目标类方法执行前执行该通知 * */ @Before(value="cut()") public void before() { System.out.println("前置通知....."); } /** * 声明返回通知并将通知应用到定义的切入点上 * 目标类方法(无异常)执行后执行该通知 * */ @AfterReturning(value="cut()") public void afterReturn() { System.out.println("返回通知....."); } /** * 声明最终通知并将通知应用到定义的切入点上 * 目标类方法(无异常或有异常)执行后执行该通知 * */ @After(value="cut()") public void after() { System.out.println("最终通知....."); } /** * 声明异常通知并将通知应用到定义的切入点上 * 目标类方法出现异常时执行该通知 */ @AfterThrowing(value="cut()",throwing="e") public void afterThrow(Exceptione) { System.out.println("异常通知....."+" 异常原因:"+e.getCause()); } /** * 声明环绕通知并将通知应用到切入点上 与上面四个方法等价需要注释之前的方法 * 方法执行前后通过环绕通知定义相应处理 * 需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();) * @param pjp* @return */ @Around(value="cut()") public Object around(ProceedingJoinPointpjp) { System.out.println("前置通知..."); Objectobject=null; try { object=pjp.proceed(); System.out.println(pjp.getTarget() +"======"+pjp.getSignature());// System.out.println("返回通知..."); } catch (Throwablethrowable) { throwable.printStackTrace(); System.out.println("异常通知..."); } System.out.println("最终通知..."); return object; } } 建议推荐使用 spring-tx maven项目导入依赖 org.springframework spring-tx 5.2.4.RELEASE org.aspectj aspectjweaver 1.9.5 xml配置

在spring.xml配置⽂件的添加事务和aop的命名空间

事务。。 xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd AOP。。。 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

开启AOP代理

配置事务管理器

配置相关的事务通知

事务传播⾏为介绍:

@Transactional(propagation=Propagation.REQUIRED)

如果有事务, 那么加⼊事务, 没有的话新建⼀个(默认情况下)

@Transactional(propagation=Propagation.NOT_SUPPORTED)

容器不为这个⽅法开启事务

@Transactional(propagation=Propagation.REQUIRES_NEW)

不管是否存在事务,都创建⼀个新的事务,原来的挂起,新的执⾏完毕,继续执⾏⽼的事务

@Transactional(propagation=Propagation.MANDATORY)

必须在⼀个已有的事务中执⾏,否则抛出异常

@Transactional(propagation=Propagation.NEVER)

必须在⼀个没有的事务中执⾏,否则抛出异常(与 Propagation.MANDATORY 相反)

@Transactional(propagation=Propagation.SUPPORTS)

如果其他 bean 调⽤这个⽅法,在其他 bean 中声明事务,那就⽤事务.

如果其他 bean 没有声明事务,那就不⽤事务.

@Transactional(propagation=Propagation.NESTED)

⽀持当前事务,如果当前事务存在,则执⾏⼀个嵌套事务,如果当前没有事务,就新建⼀个事务。

配置AOP

配置后的模板

注解实现

也可使用注解,将事务配置替换,或者可以不更改。但要确定命名方法不在上面的扫描中

只需要在类上:@Transactional(propagation = Propagation.REQUIRED)

@Service public class AccountService { @Resource private AccountDaoImpl accountDao; /** * 事务的使用 * @param outId * @param money * @param inId */ @Transactional(propagation = Propagation.REQUIRED) public void updateTranfer(Integer outId, Double money, Integer inId) { // 支出操作 Integer row = accountDao.outMoney(outId, money); int i = 1/0; // 收入操作 Integer row2 = accountDao.inMoney(inId, money); // 判断是否操作成功 if (row == 1 && row2 == 1) { System.out.println("转账成功!"); } else { System.out.println("转账失败 !"); } } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3