【javaweb】学习日记Day13 - AOP 事务管理 切入点 连接点

目录

一、完善解散部门功能

二、spring 事务 

(1)@Transactional 事务管理

① rollbackFor 控制异常类型

② propagation 事务传播控制

1、定义解散部门操作日记

三、AOP基础

1、概述

2、快速入门

(1)案例:统计各个业务层方法的执行耗时

① 引入AOP依赖

② 建立AOP类

3、AOP核心概念

(1)AOP的执行流程 

四、AOP进阶

 1、通知类型

(1)@PointCut 公共切点表达式 

2、通知顺序

3、切入点表达式

(1)execution

(2)@annotation

4、连接点

五、AOP案例

(1)引入AOP依赖

(2)在数据库里建操作日记记录表

(3)定义数据库表对应的实体类

(4)定义对应的Mapper接口

(5)定义注解

(6)完成AOP类编写

(7)给需要匹配的增删改方法加上注解


一、完善解散部门功能

删除部门时,应该把部门下相应的员工也一并删除

注意:数据库不推荐物理外键,一般都是逻辑外键

step 1:改写Dept的Service层

    @Overridepublic void delete(Integer id) {deptMapper.deleteById(id); //根据部门id删除部门empMapper.deleteByDeptId(id); //根据部门id删除该部门下对应的员工}

step 2:完善Emp的mapper层

    //根据部门id删除对应员工@Delete("delete from emp where dept_id = #{deptId}")void deleteByDeptId(Integer deptId);

二、spring 事务 

当中间出现异常时,会出现,异常前的语句执行了,但是异常后的语句没有成功执行,会出现bug

因此我们需要进行事务回滚(事务回滚指的是当发生错误或异常时,事务能够自动地撤销已经执行的操作,返回到事务开始之前的状态) 

(1)@Transactional 事务管理

① rollbackFor 控制异常类型

  • 作用:控制出现何种异常类型,事务回滚
  • 默认情况下,只有出现RuntimeException才会回滚异常
    @Transactional(rollbackFor = Exception.class)@Overridepublic void delete(Integer id) {deptMapper.deleteById(id); //根据部门id删除部门empMapper.deleteByDeptId(id); //根据部门id删除该部门下对应的员工}

② propagation 事务传播控制

作用:当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制

属性值含义
REQUIRED(默认值)需要事务,有则加入,无则创建新事务
REQUIRES_NEW

需要新事务,无论有无事务,总是创建新事务,

当我们不希望事务相互影响时使用

这里我们运用一个案例进行详细说明:

要求解散部门时,无论解散成功or失败,都要记录操作日志

1、定义解散部门操作日记

(1)DeptLog实体类

// 解散部门日志
@Data //@Data注解的主要作用是提高代码的简洁,使用这个注解可以省去实体类中大量的get()、 set()、 toString()等方法
@NoArgsConstructor
@AllArgsConstructor
public class DeptLog {private Integer id;private LocalDateTime createTime;private String description;
}

(2)DeptLogService

public interface DeptLogService {//插入日志void insert(DeptLog deptLog);
}
public class DeptLogServiceImpl implements DeptLogService {@Autowiredprivate DeptLogMapper deptLogMapper;@Transactional@Overridepublic void insert(DeptLog deptLog) {deptLogMapper.insert(deptLog);}
}

(3)DeptLogMapper

@Mapper
public interface DeptLogMapper {@Insert("insert into dept_log(create_time,description) values (#{createTime},#{description})")void insert(DeptLog deptLog);
}
  • 因为若不指定propagation的值,默认为REQUIRED,即为若需要新事务,则无需再创建,直接加入已有事务,也就是insert方法加入到delete方法的事务中
  • 此时若delete事务出现异常,整个事务发生回滚,因此也不会有日志记录
  • 因此我们需要在insert上指定propagation的值为REQUIRES_NEW,即若需要新事务,则再开一个新事务,当delete事务出现异常时,只在delete事务中发生回滚,insert事务正常运行,日志正常记录
    @Transactional(rollbackFor = Exception.class)@Overridepublic void delete(Integer id) {try{deptMapper.deleteById(id); //根据部门id删除部门empMapper.deleteByDeptId(id); //根据部门id删除该部门下对应的员工}finally {DeptLog deptLog = new DeptLog();deptLog.setCreateTime(LocalDateTime.now());deptLog.setDescription("本次解散的是"+id+"号部门");deptLogService.insert(deptLog);}}
    @Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void insert(DeptLog deptLog) {deptLogMapper.insert(deptLog);}

三、AOP基础

1、概述

       开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决

AOP 的主要作用就是在不侵入原有程序的基础上实现对原有功能的增强

2、快速入门

(1)案例:统计各个业务层方法的执行耗时

① 引入AOP依赖
        <!--AOP--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
② 建立AOP类

 

@Slf4j
@Component
@Aspect // AOP类
public class TimeAspect {@Around("execution(* com.itroye.service.*.*(..))") //切入点表达式public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//记录开始时间long begin = System.currentTimeMillis();//调用原始方法Object result = joinPoint.proceed();//记录结束时间long end = System.currentTimeMillis();log.info(joinPoint.getSignature()+"执行耗时:{}ms",end-begin);return result;}
}

3、AOP核心概念

  • 连接点 JoinPoint:可以被AOP控制的方法

  • 通知 Advice:指重复的逻辑,也就是共性功能

  • 切入点 PointCut:匹配连接点的条件,通知仅会在切入点方法执行时被应用

  • 切面 Aspect:通知+切入点

  • 目标对象 Target:通知所应用的对象

(1)AOP的执行流程 

 运行的不是原始的目标对象,而是基于目标对象所生成的代理对象

四、AOP进阶

 1、通知类型

  • 前置通知(@Before):在目标方法执行前执行的通知。
  • 后置通知(@After):在目标方法执行后执行的通知,无论目标方法是否抛出异常都会执行。
  • 返回通知(@AfterReturning):在目标方法正常返回后执行的通知。
  • 异常通知(@AfterThrowing):在目标方法抛出异常后执行的通知。
  • 环绕通知(@Around):在目标方法执行前后都可以执行的通知,可以控制目标方法的执行。

注意:

  • @Around 需要调用ProceedingJoinPoint.proceed()让原始方法执行,其他通知不需要目标方法执行
  •  @Around 的返回值必须是Object,来接收原始方法的返回值

(1)@PointCut 公共切点表达式 

 

@Slf4j
@Component
@Aspect
public class MyAspect1 {@Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")public void pt(){}@Before("pt()")public void before(){log.info("before ...");}@Around("pt()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {log.info("around before ...");//调用目标对象的原始方法执行Object result = proceedingJoinPoint.proceed();log.info("around after ...");return result;}@After("pt()")public void after(){log.info("after ...");}@AfterReturning("pt()")public void afterReturning(){log.info("afterReturning ...");}@AfterThrowing("pt()")public void afterThrowing(){log.info("afterThrowing ...");}
}

2、通知顺序

3、切入点表达式

(1)execution

 

(2)@annotation

用于匹配标识有特定注解的方法

新建注解

@Retention(RetentionPolicy.RUNTIME) //运行时机
@Target(ElementType.METHOD) //该注解可以定义在方法上
public @interface MyLog {
}

在需要切入的切入点方法上加上该注解

然后在切面处 @annotation(注解全类名),即可匹配拥有该注解的方法

4、连接点

连接点就是可以被AOP控制的方法

  • 在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等
    • 对于@Around通知,获取连接点信息只能用ProceedingJoinPoint
    • 对于其他四种通知,获取连接点信息只能用JoinPoint,它是ProceedingJoinPoint的父类型

五、AOP案例

思路分析:

  • 需要对所有Service的增删改方法添加统一功能,使用AOP技术  运用@Around环绕通知
  • 由于增删改方法名无规律,自定义@Log注解完成目标方法匹配

 

(1)引入AOP依赖

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

(2)在数据库里建操作日记记录表

-- 操作日志表
create table operate_log(id int unsigned primary key auto_increment comment 'ID',operate_user int unsigned comment '操作人ID',operate_time datetime comment '操作时间',class_name varchar(100) comment '操作的类名',method_name varchar(100) comment '操作的方法名',method_params varchar(1000) comment '方法参数',return_value varchar(2000) comment '返回值',cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';

(3)定义数据库表对应的实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {private Integer id; //IDprivate Integer operateUser; //操作人IDprivate LocalDateTime operateTime; //操作时间private String className; //操作类名private String methodName; //操作方法名private String methodParams; //操作方法参数private String returnValue; //操作方法返回值private Long costTime; //操作耗时
}

(4)定义对应的Mapper接口

@Mapper
public interface OperateLogMapper {//插入日志数据@Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +"values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")public void insert(OperateLog log);}

(5)定义注解

@Retention(RetentionPolicy.RUNTIME) //运行时机
@Target(ElementType.METHOD) //该注解可以定义在方法上
public @interface Log {
}

(6)完成AOP类编写

@Slf4j
@Component
@Aspect //切面类
public class LogAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate OperateLogMapper operateLogMapper;@Around("@annotation(com.itroye.anno.Log)")public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {//操作人ID - 当前登录员工ID//获取请求头中的jwt令牌, 解析令牌String jwt = request.getHeader("token");Claims claims = JwtUtils.parseJWT(jwt);Integer operateUser = (Integer) claims.get("id");//操作时间LocalDateTime operateTime = LocalDateTime.now();//操作类名String className = joinPoint.getTarget().getClass().getName();//操作方法名String methodName = joinPoint.getSignature().getName();//操作方法参数Object[] args = joinPoint.getArgs();String methodParams = Arrays.toString(args);long begin = System.currentTimeMillis();//调用原始目标方法运行Object result = joinPoint.proceed();long end = System.currentTimeMillis();//方法返回值String returnValue = JSONObject.toJSONString(result);//操作耗时Long costTime = end - begin;//记录操作日志OperateLog operateLog = new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,returnValue,costTime);operateLogMapper.insert(operateLog);log.info("AOP记录操作日志: {}" , operateLog);return result;}}

(7)给需要匹配的增删改方法加上注解

这里是给Controller层加的注释!

保证返回值都是Result

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

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

相关文章

RabbitMQ初入门

1、RabbitMQ是什么 RabbitMQ是“实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff08;亦称面向消息的中间件&#xff09;。RabbitMQ服务器是用Erlang语言编写的&#xff0c;而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均…

STM32的bootloader程序(通过串口更新STM32应用程序)

1 什么是bootloader&#xff1f; Bootloader&#xff0c;也被称为引导加载程序&#xff0c;是操作系统启动过程中的一个重要组成部分。它是存储在非易失性存储器中的一段小程序&#xff0c;负责在操作系统内核运行之前加载并启动一些必要的系统组件。 当计算机开机后&#xff0…

ICC2: 如何在显示GUI操作产生的命令

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 ICC2&#xff1a;自定义快捷键和菜单 VIEW -> Perference -> Global Settings 把display commands in logging console 下面几个都勾上即可。

css:button实现el-radio效果

先看最终效果&#xff1a; ​​​ 思路&#xff1a; 一、 首先准备好按钮内容&#xff1a;const a [one,two,three] 将按钮循环展示出来&#xff0c;并设置一些样式&#xff0c;将按钮背景透明&#xff1a; <button v-for"(item,index) in a" :key"in…

实战之巧用header头

案例&#xff1a; 遇到过三次 一次是更改accept&#xff0c;获取到tomcat的绝对路径&#xff0c;结合其他漏洞获取到shell。 一次是更改accept&#xff0c;越权获取到管理员的MD5加密&#xff0c;最后接管超管权限。 一次是更改accept&#xff0c;结合参数获取到key。 这里以越…

如何选购适合自己的内衣洗衣机?性价比高内衣洗衣机推荐

内衣洗衣机&#xff0c;在几年前或许是个新事物&#xff0c;但近两年却是成为了很普遍的家电产品&#xff0c;由于近几年内衣洗衣机需求量的增加&#xff0c;我们在商场的电子产品区都能见到它&#xff0c;就像普通的家庭洗衣机那样&#xff0c;直接摆在展台上销售&#xff0c;…

VPS是什么?详解亚马逊云科技Amazon Lightsail(VPS)虚拟专用服务器

2006年&#xff0c;南非开普敦&#xff0c;亚马逊推出了WBS&#xff0c;以网络服务的形式向企业提供基础的IT服务。亚马逊云科技的一小步&#xff0c;在无数技术更迭&#xff0c;天才设计师和程序员的努力与基础设施建设的完善之下成为了人类科技进展的一大步。 亚马逊云科技可…

【Spring Boot 源码学习】RedisAutoConfiguration 详解

Spring Boot 源码学习系列 RedisAutoConfiguration 详解 引言往期内容主要内容1. Spring Data Redis2. RedisAutoConfiguration2.1 加载自动配置组件2.2 过滤自动配置组件2.2.1 涉及注解2.2.2 redisTemplate 方法2.2.3 stringRedisTemplate 方法 总结 引言 上篇博文&#xff0…

携手云栖,共望未来

&#x1f4a8;随着信息技术的迅猛发展&#xff0c;云计算已成为推动数字经济发展的重要驱动力之一。在这个领域中&#xff0c;云栖大会无疑是中国乃至全球最重要的盛会之一。云栖大会的历史可以追溯到2009年的地方网站峰会&#xff0c;随着时间的推移&#xff0c;它逐渐演变为阿…

如何在Instagram和kol展开合作

网红营销已经演变成一个由品牌、MCN机构、红人和消费者组成的复杂生态系统&#xff0c;并在某种程度上重新定义了当今社交媒体时代营销和广告的本质。在这个情况下&#xff0c;品牌找红人进行营销推广已经成为大势&#xff0c;而最能体现网红营销发展的莫过于Instagram这个平台…

数据结构(三):栈及面试常考的算法

一、栈介绍 1、定义 栈也是一种数据呈线性排列的数据结构&#xff0c;不过在这种结构中&#xff0c;我们只能访问最新添加的数据。从栈顶放入元素的操作叫入栈&#xff0c;取出元素叫出栈。 2、优缺点及使用场景 优点&#xff1a;高效的操作、简单易用、空间效率高等 缺点&…

【SpringSecurity】快速入门—通俗易懂

目录 1.导入依赖 2.继承WebSecurityConfigurerAdapter 3.实现UserDetailsService 4.记住我 5.用户注销 6.CSRF理解 7.注解功能 7.1Secured 7.2PreAuthorized 7.3PostAuthorized 7.4PostFilter 7.5ZPreFilter 8.原理解析 1.导入依赖 首先&#xff0c;在pom.xml文…

计算机网络-IP地址

文章目录 子网划分定长子网划分子网划分的方法子网掩码 可变长子网划分 无类别编址网络前缀路由聚合 特殊用途的IP地址专用网络地址链路本地地址运营商级NAT共享地址用于文档的测试网络地址 IP地址的规划和分配IP地址的规划和分配方法IP地址的规划和分配实例 子网划分 定长子网…

文章分类管理接口

目录 前言 新建表 获取文章分类列表接口 初始化路由模块 将路由对象导出并使用 初始化路由对象处理函数 修改路由代码 导入数据库 定义sql语句 调用db.query() 完整的获取文章分类列表处理函数 新增文章分类接口 定义路由和处理函数 验证表单数据 查询分类名称与…

UDP网络编程的接受与发送信息

/发送端B>可以接受数据 public class UDPSenderB {public static void main(String[] args) throws IOException {//创建一个DatagramSocket 对象&#xff0c;准备发送和接受数据DatagramSocket socket new DatagramSocket(9998);//将需要发送的数据&#xff0c;封装到Data…

空号检测API如何助力于提高客户关系管理

引言 在现代商业世界中&#xff0c;客户关系管理已经成为企业成功的关键要素之一。CRM不仅涉及到如何吸引新客户&#xff0c;还包括如何维护并与现有客户建立持久而有益的关系。在这个过程中&#xff0c;通信是至关重要的。为了确保您的客户数据库保持最新和准确&#xff0c;空…

navicat15 恢复试用方法

1.运行&#xff0c;输入regedit&#xff0c;打开注册表 2.注册表中搜索 HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPremium&#xff0c;删除下面的Registration15XCS文件夹 3.注册表中再搜索 HKEY_CURRENT_USER\Software\Classes\CLSID 然后拉到文件夹目录的最后&#x…

「永不失联」产品创新与升级系列发布,预约直播“即将发车”

数字化浪潮下&#xff0c;北斗时空智能正成为我国重要的新型基础设施。 通过将卫星定位精度提升至厘米级乃至毫米级&#xff0c;时空智能满足了数字化时代智能驾驶、共享出行、智慧城市等多种智能终端对时空信息的爆发式增长需求&#xff0c;同步印证着测绘地理信息领域的技术应…

什么是Vue.js中的指令(directive)?举例说明一些常见的指令。

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

Azure机器学习 - 使用与Azure集成的Visual Studio Code实战教程

本文介绍如何启动远程连接到 Azure 机器学习计算实例的 Visual Studio Code。 借助 Azure 机器学习资源的强大功能&#xff0c;使用 VS Code 作为集成开发环境 (IDE)。 在VS Code中将计算实例设置为远程 Jupyter Notebook 服务器。 关注TechLead&#xff0c;分享AI全维度知识。…