Spring-AOP与声明式事务

为什么要用AOP

①现有代码缺陷
针对带日志功能的实现类,我们发现有如下缺陷:
对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
附加功能分散在各个业务功能方法中,不利于统一维护
②解决思路
解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。
解决方案一–代理模式:
在这里插入图片描述
静态代理

public class CalculatorStaticProxy implements Calculator {
// 将被代理的目标对象声明为成员变量
private Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
@Override
public int add(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
// 通过目标对象来实现核心业务逻辑
int addResult = target.add(i, j);
System.out.println("[日志] add 方法结束了,结果是:" + addResult);return addResult;
}
}

缺点:静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。
动态代理:写一个工厂类实现附加功能

public class ProxyFactory {private Object target;public ProxyFactory(Object target) {this.target = target;}public Object getProxy(){//jdk动态代理 通过反射实现动态代理/**其中有三个参数:* 1、classLoader:加载动态生成的代理类(ProxyFactory)的类加载器* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口*/ClassLoader classLoader = target.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {/*** proxy:代理对象* method:代理对象需要实现的方法,即其中需要重写的方法* args:method所对应方法的参数*/Object result = null;try {System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));result = method.invoke(target, args);System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);} catch (Exception e) {e.printStackTrace();System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());} finally {System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");}return result;}};return Proxy.newProxyInstance(classLoader, interfaces,invocationHandler);}	

动态代理方法
1.jdk动态代理
2.cglib动态代理
在这里插入图片描述

动态代理是AOP的底层实现原理!

AOP的基础

AOP概念及相关术语

概述
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
相关术语
横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方
法进行多个不同方面的增强。
通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
前置通知:在被代理的目标方法前执行
返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
异常通知:在被代理的目标方法异常结束后执行(死于非命)
后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所
有位置
切面
封装通知方法的类
目标
被代理的目标对象。
代理
向目标对象应用通知之后创建的代理对象。
连接点
在这里插入图片描述
切入点
定位连接点的方式。
如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。
Spring 的 AOP 技术可以通过切入点定位到特定的连接点。
切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
作用:
**简化代码:**把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
**代码增强:**把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。

基于注解的AOP

在这里插入图片描述
举个栗子
依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>

目标资源
接口

public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}

实现类

@Component
public class CalculatorPureImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部 result = " + result);
return result;
}
}

创建切面类并配置

// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {
//execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))为切入点表达式
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参
数:"+args);
}
@After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
@AfterReturning(value = "execution(*
com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结
果:"+result);
}
@AfterThrowing(value = "execution(*
com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
//环绕通知 相当于四种普通通知的组合
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行
result = joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}

切入点表达式的具体格式
在这里插入图片描述
切面的优先级
通过Order注解的value属性设置

声明式事务

编程式事务概念
事务功能的相关操作全部通过自己编写代码来实现

try {
// 开启事务:关闭事务的自动提交
conn.setAutoCommit(false);
// 核心操作
// 提交事务
conn.commit();
}catch(Exception e){
// 回滚事务
conn.rollBack();
}finally{
// 释放数据库连接
conn.close();
}

编程式的实现方式存在缺陷
细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复
用。
声明式事务
既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。
封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。
好处1:提高开发效率
好处2:消除了冗余的代码
好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化

基于注解的声明式事务

在spring配置中开启事务驱动

<!--
开启事务的注解驱动
通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务
-->
<!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就
是这个默认值,则可以省略这个属性 -->
<tx:annotation-driven transaction-manager="transactionManager" />

添加事务注解
因为service层表示业务逻辑层,一个方法表示一个完成的功能,因此处理事务一般在service层处理 在Service对应的方法上添加注解@Transactional
@Transactional注解标识的位置
@Transactional标识在方法上,咋只会影响该方法
@Transactional标识的类上,咋会影响类中所有的方法
事务属性:回滚策略

声明式事务默认只针对运行时异常回滚,编译时异常不回滚。
可以通过@Transactional中相关属性设置回滚策略
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
示例:

@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
System.out.println(1/0);
}

事务属性:事务隔离级别
隔离级别一共有四种:
读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
读已提交:READ COMMITTED、
要求Transaction01只能读取Transaction02已提交的修改。
可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
在这里插入图片描述
事务属性:事务传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
示例:
通过@Transactional中的propagation属性设置事务传播行为
REQUIRED(必需): 假设扣款和存款方法都标记为 REQUIRED。这意味着如果它们被一个非事务性的方法调用,例如一个简单的按钮点击事件处理器,它们会在新的事务中执行。但如果它们被一个已经在事务中运行的方法调用,例如转账操作,它们会加入这个已存在的事务。这就确保了转账操作中的扣款和存款要么都成功,要么都不成功,从而维护了数据的一致性。
REQUIRES_NEW(需要新的): 现在假设我们将存款操作改为 REQUIRES_NEW。这意味着无论存款方法是如何被调用的,它都会在一个新的事务中执行。这可能用于某些特殊场景,比如即使扣款失败,银行也希望确保某些类型的存款(如利息支付)总是执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/183639.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TOUGH系列软件解读

TOUGH系列软件是由美国劳伦斯伯克利实验室开发的&#xff0c;旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同&#xff0c;TOUGH系列软件采用模块化设计和有限积分差网格剖分方法&#xff0c;通过配合不同状态方程&#xff08;EOS模…

同旺科技 分布式数字温度传感器

内附链接 1、数字温度传感器 主要特性有&#xff1a; ● 支持PT100 / PT1000 两种铂电阻&#xff1b; ● 支持 2线 / 3线 / 4线 制接线方式&#xff1b; ● 支持5V&#xff5e;17V DC电源供电&#xff1b; ● 支持电源反接保护&#xff1b; ● 支持通讯波特率1200bps、2…

使用Java对yaml和properties互转,保证顺序、实测无BUG版本

使用Java对yaml和properties互转 一、 前言1.1 顺序错乱的原因1.2 遗漏子节点的原因 二、优化措施三、源码 一、 前言 浏览了一圈网上的版本&#xff0c;大多存在以下问题&#xff1a; 转换后顺序错乱遗漏子节点 基于此进行了优化&#xff0c;如果只是想直接转换&#xff0c…

1.3 排序算法

1.1 冒泡排序 public class BubbleSort {public static void main(String[] args) {int[] arr {133,322,13,444,54,621,174,18,19,2};System.out.println(Arrays.toString(arr));BubSort(arr);System.out.println(Arrays.toString(arr));}//冒泡排序public static void BubSo…

21.Oracle的程序包(Package)

Oracle的程序包Package 一、Package的概述1、什么是Oracle11g的Package2、Package的作用是什么3、常见的系统内置Package 二、创建Package的相关语法1、Package的创建语法2、Package的删除3、具体案例4、Package的使用5、与Package相关的其他语法 三、常见内置程序包的使用1、…

element-plus el-dialog 弹窗隐藏遮罩并且可以控制弹窗后的元素、点击、滚动、其他事件操作等

场景 el-dialog 隐藏遮罩并且可以控制弹窗后的元素、点击、滚动、其他事件操作&#xff0c;比如一个弹窗打开了&#xff0c;我要能控制弹窗后面的滚动、点击等等一系列事件。 修改方法 首先我们需要隐藏弹窗遮罩 :modal"false"&#xff0c;并且给 el-dialog 弹窗…

Image Super-Resolution with Text Prompt Diffusion

Image Super-Resolution with Text Prompt Diffusion (Paper reading) Zheng Chen, Shanghai Jiao Tong University, arXiv23, Code, Paper 1. 前言 受多模态方法和文本提示图像处理进步的启发&#xff0c;我们将文本提示引入图像SR&#xff0c;以提供退化先验。具体来说&am…

2023.11.30 homework

兴趣最重要了&#xff0c;没兴趣不喜欢勉强带来的苦楚&#xff0c;并不能促使变好变优秀。 虽然我们的社会环境依旧很残酷&#xff0c;各种各样的硬性要求。

计算机网络(一)| 概述 因特网 性能 协议基本

文章目录 1. 因特网组成1.1 四元素组成1.2 二元素组成1.3 核心部分 2.计算机网路的功能3. 几种不同类别的网络4 性能指标5 网路协议5.1网络体系结构 6 PDU 互联网&#xff08;或因特网&#xff09;之所以能够向用户提供服务&#xff0c;是因为互联网具有两个重要基本特点 连通性…

Java多线程

20.1线程介绍 世间有很多工作都是可以同时完成的。例如&#xff0c;人体可以同时进行呼吸、血液循环、思考问题等活动&#xff1b;用户既可以使用计算机听歌&#xff0c;也可以使用它打印文件。同样&#xff0c;计算机完全可以将多种活动同时进行&#xff0c;这种思想放在 Java…

Grafana部署与Zabbix集成,搭建开源IT监控平台

Grafana部署与Zabbix集成 目前在一家公司主要是网络、运维、IT支持&#xff0c;每次需要检查服务器状态都是需要手动登录系统进行查看&#xff0c;因此想着部署一套监控系统&#xff0c;功能上需要实现监控、可视化、告警等。由于预算没有&#xff0c;服务器资源倒是有空闲的&a…

智能工厂是什么?

今天就聊聊企业智能工厂的打造&#xff0c;企业想实现数字化转型建立智能工厂&#xff0c;就需要先建设数字化车间&#xff0c;可以说数字化车间是建设智能工厂的重要一环&#xff0c;智能工厂的基础是数字化车间。数字化车间可以实现企业生产过程中车间计划调度、工艺执行管理…

基于python 医院预约挂号系统-计算机毕业设计源码24802

摘 要 随着互联网时代的到来&#xff0c;同时计算机网络技术高速发展&#xff0c;网络管理运用也变得越来越广泛。因此&#xff0c;建立一个基于django 医院预约挂号系统 &#xff0c;会使&#xff1b;医院预约挂号系统的管理工作系统化、规范化&#xff0c;也会提高平台形象&a…

基于javaweb的宠物服务商城系统设计与开发

摘 要 最近几年以来&#xff0c;宠物在人们的日常生活中所占的地位越来越重要了&#xff0c;它们不仅仅是我们的朋友&#xff0c;也成为了我们家庭中的一份子。21世纪&#xff0c;信息技术飞速发展&#xff0c;计算机行业日新月异&#xff0c;极大地带动了信息的流动&#xff…

顺丰JAVA开发一面—面试实战经验分析【已通过】

文章目录 面试总结面试开始项目相关基础知识反问环节 顺丰JAVA开发一面面试过程中的问题确实涵盖了很多方面&#xff0c;从项目架构到基础知识再到具体技术细节都有所涉及。 面试官的提问风格也是比较开放的&#xff0c;注重考察面试者的深度理解和解决问题的能力。以下是对每个…

c语言:回文字符串

题目&#xff1a; 思路&#xff1a; 创建一个字符数组&#xff0c;然后判断字符串长度&#xff0c;用循环&#xff0c;看对应字符是否相等&#xff0c;相等则输出&#xff0c;不相等则将对应字符ascll较大的改成ascll较小的&#xff08;题目要求字典最小的情况&#xff09;。…

手势识别4:C/C++实现手部检测和手势识别(含源码下载)

手势识别4&#xff1a;C/C实现手部检测和手势识别(含源码下载) 目录 手势识别4&#xff1a;C/C实现手部检测和手势识别(含源码下载) 1. 前言 2. 手势识别模型&#xff08;YOLOv5&#xff09; &#xff08;1&#xff09;手势识别模型训练 &#xff08;2&#xff09;将Pyto…

GoLong的学习之路,进阶,Redis

这个redis和上篇rabbitMQ一样&#xff0c;在之前我用Java从原理上进行了剖析&#xff0c;这里呢&#xff0c;我做项目的时候&#xff0c;也需要用到redis&#xff0c;所以这里也将去从怎么用的角度去写这篇文章。 文章目录 安装redis以及原理redis概念redis的应用场景有很多red…

【开源】基于Vue+SpringBoot的创意工坊双创管理系统

项目编号&#xff1a; S 049 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S049&#xff0c;文末获取源码。} 项目编号&#xff1a;S049&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、…

nodejs 沙盒逃逸

1.[GFCTF 2021]ez_calc 一道很有意思的一道nodejs的题 沙箱逃逸和绕过&#xff1a; F12 看源码 if(req.body.username.toLowerCase() ! admin && req.body.username.toUpperCase() ADMIN && req.body.passwd admin123){ // 登录成功&am…