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

news2024/12/24 2:27:09

目录

一、完善解散部门功能

二、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层

    @Override
    public 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)
    @Override
    public 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 {

    @Autowired
    private DeptLogMapper deptLogMapper;

    @Transactional
    @Override
    public 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)
    @Override
    public 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)
    @Override
    public 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; //ID
    private Integer operateUser; //操作人ID
    private 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 {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private 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.coloradmin.cn/o/1148955.html

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

相关文章

c++拷贝构造与赋值运算符重载

目录 目录&#xff1a; 1&#xff1a;拷贝构造 2&#xff1a;赋值运算符重载 前言&#xff1a;在上一章我们已经学习过了&#xff0c;构造与析构这两个默认成员函数了&#xff0c;接下来让我们一起来学习另外两个重要的默认成员函数。 首先让我们来讲一下默认成员函数这个概念…

[架构之路-245/创业之路-76]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 企业资源管理计划ERP

目录 前言&#xff1a; 一、企业信息化的结果&#xff1a;常见企业信息化软件 1.1 企业资源管理计划 1.1.1 什么是ERP&#xff1a;企业最常用的信息管理系统 1.1.2 ERP的演进过程 1.1.3 EPR模块 1.1.4 EPR五个层级 1.1.5 企业EPR业务总体流程图 1.1.6 什么类型的企业需…

微信小程序云开发如何优雅的实现模糊查询

微信官方自从推出微信小程序云开发之后&#xff0c;让小程序开发的门槛再次降低&#xff0c;你可以不需要掌握任何后端的语言和数据库&#xff0c;有一些js的基本功&#xff0c;就可以完成一个完整带前后端交互功能的小程序项目。今天我们介绍一个日常开发中非常常见的需求&…

转化率(CVR)是什么意思,怎么计算和提高转化率?

转化率指的是在一定时间范围内&#xff0c;企业在线上或线下从事某项营销推广&#xff08;比如竞价广告&#xff09;活动时&#xff0c;成功完成转化行为的次数占推广信息总点击次数的百分比率。它是产品营销推广中的一个重要数据指标&#xff0c;转化率越高&#xff0c;成本越…

【教3妹学编辑-算法题】H 指数 II

3妹&#xff1a;2哥早啊&#xff0c; 新的一周开始了&#xff0c;奥利给&#xff01;&#xff01;&#xff01; 2哥 :3妹&#xff0c;今天起的很早嘛&#xff0c;精神也很饱满。 3妹&#xff1a;昨天睡的早&#xff0c;早睡早起好身体&#xff01; 2哥&#xff1a;既然离时间还…

不定长顺序表2

接下来我们看怎么完成不定长顺序表的代码实现 这里先加一个头文件&#xff0c;名字叫dsqlist.h&#xff0c;存放不定长顺序表的函数定义与声明 然后建立一个名字叫dsqlist.cpp的源文件&#xff0c;跟其头文件配对成一对&#xff0c;(也可以叫别的名字不配对&#xff09;&…

Java 中的 synchronized 同步锁

导致线程安全问题的根本原因在于&#xff0c;存在多个线程同时操作一个共享资源&#xff0c;要想解决这个问题&#xff0c;就需要保证对共享资源访问的独占性&#xff0c;因此人们在Java中提供了synchronized关键字&#xff0c;我们称之为同步锁&#xff0c;它可以保证在同一时…

力扣每日一题83:删除排序数组中的重复元素

题目描述&#xff1a; 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2]示例 2&#xff1a; 输入&#xff1a;head [1,1,2…

Day14力扣打卡

打卡记录 H 指数&#xff08;二分&#xff09; 链接 以最大值 x 为分割点的正整数数轴上&#xff0c;满足&#xff1a; 少于等于 x 的数值必然满足条件&#xff1b;大于 x 的数值必然不满足。 采用右边界二分查找&#xff0c;寻找满足条件的最大 H 指数要求。 class Soluti…

shell脚本函数(极其粗糙版)

分界点&#xff1a;以下内容需要更改&#xff0c;正常放假更改 函数&#xff1a; 1、把整个命令序列按照格式写在一起 2、可以方便的重复使用的命令序列 使用函数可以避免代码重复 函数可以将大的工程分割为诺干小的功能模块&#xff0c;可以随时调用&#xff0c;代码的可读…

Windows一键添加命名前缀(文件)

温馨提示&#xff1a;使用前建议先进行测试和原文件备份&#xff0c;避免引起不必要的损失。 &#xff08;一&#xff09;需求描述 在上班摸鱼的我正准备打开手机刷会儿CSDN论坛&#xff0c;老板发给我一个压缩包并要求我给里面所有的文件的名称添加一个前缀”大项目_”。我本…

协同过滤音乐推荐系统

摘 要 信息化爆炸的时代&#xff0c;互联网技术的指数型的增长&#xff0c;信息化程度的不断普及&#xff0c;社会节奏在加快&#xff0c;每天都有大量的信息扑面而来&#xff0c;人们正处于数字信息化世界。数字化的互联网具有便捷性&#xff0c;传递快&#xff0c;效率高&am…

windows系统命令常用命令(常看当前路径等)

打开cmd命令行终端 按下 winr 快捷键&#xff0c;输入cmd&#xff0c;点击确定 查看当前路径 输入命令 chdir 查看目录下文件 输入命令 dir //查看当前目录下的文件&#xff0c;类似于linux下的ls cd /d e:\software //跳转到其他硬盘的其他文件夹&#xff0c;注意此处…

Linux权限的概念

文章目录 Linux权限管理01.文件访问者的分类&#xff08;人&#xff09;02.文件类型和访问权限&#xff08;事物属性&#xff09;03.文件权限值的表示方法04. 调整权限&#xff08;文件访问权限的相关设置方法&#xff09; ch系列是改变文件性质的指令&#xff1b;file指令可以…

CS224W2.3——传统基于特征的方法(图层级特征)

前两篇中我们讨论了节点层级的特征表示、边层级的特征表示&#xff1a; CS224W2.1——传统基于特征的方法(节点层级特征)CS224W2.2——传统基于特征的方法(边层级特征) 在这篇中&#xff0c;我们将重点从整个图中提取特征。换句话说&#xff0c;我们想要描述整个图结构的特征…

2.27每日一题(定积分求面积,旋转体体积)

1、遇到求面积、定积分的问题&#xff0c;先画图&#xff1a; &#xff08;1&#xff09;抛物线&#xff08;可正可负&#xff0c;所以抛物线函数需要有 a 变量&#xff09; &#xff08;2&#xff09;过两个点 2、定积分求面积有三种情况对应三条公式&#xff1a; &#xff0…

H5游戏源码分享-像素小鸟游戏(类似深海潜艇)

H5游戏源码分享-像素小鸟游戏&#xff08;类似深海潜艇&#xff09; 点击屏幕控制小鸟的飞行高度 整个小游戏就用JS完成 项目地址&#xff1a;https://download.csdn.net/download/Highning0007/88483228 <!DOCTYPE HTML> <html><head><meta http-equiv…

【C++】智能指针总结:auto_ptr、unique_ptr、share_ptr、weak_ptr(技术介绍 + 代码实现)

文章目录 0. 概述智能指针&#xff0c;智能在哪儿&#xff1f;RAII 的介绍四个智能指针的特点&#xff1a; 1. auto_ptr&#xff08;C98&#xff09;&#x1f40e;核心功能的简单实现 2. unique_ptr&#xff08;C11&#xff09;&#x1f40e;核心功能的简单实现 3. shared_ptr&…

LSMW应用

案 执行第二行 第六步直接跳过.

【2023MathorCup大数据竞赛】B题完整解答过程(思路+模型文档+代码+结果)

B题完整解答过程 写在最后技术文档&#xff08;部分&#xff09;问题分析假设符号说明1 基于自适应ARIMA-LR模型的需求量预测&#xff08;问题一第一小问&#xff09;1.1 ARIMA模型的建立1.2 LR模型的建立1.3 自适应混合ARIMA-LR模型的建立1.4 ARIMA模型的求解1.5 LR模型的求解…