【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层

    @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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

测试时间函数

clock():测试时间函数 测试程序开始运行(即从main开始)到执行当前行需要的时间值,单位毫秒.需要引用time.h 例如&#xff1a;需要需要测试输出0~100需要的时间可以用如下代码&#xff1a; #include<stdio.h> #include<time.h>//单位是毫秒 int main() {clock_t c1…

Android 3D Launcher锁定IMU界面

故事背景&#xff1a; 最近工厂反馈由于VR设备老化测试完成之后&#xff0c;变绿界面不明显&#xff0c;只占3D系统一部分,每次需要戴头盔&#xff0c;才能确定老化完成。导致工厂效率变低&#xff0c;如果后期产能变大&#xff0c;效率更低。 1、针对以上需求我们需要拆分 1、…

【SwiftUI系列】2.@State属性包装器

上一篇介绍了不透明返回类型&#xff0c;这篇介绍下属性包装器。 这里需要先区别下属性包装器和属性代理两个概念&#xff0c;因为在网络上有不同的称呼。 属性包装器&#xff0c;英文PropertyWrapper。 属性代理&#xff0c;英文PropertyDelegate。 1.属性包装器和属性代理…

汇编语言-div指令溢出问题

汇编语言-div指令溢出问题 8086CPU中被除数保存在ax(16位)或ax和dx&#xff08;32位&#xff09;中&#xff0c;如果被除数为16位&#xff0c;进行除法运算时al保存商&#xff0c;ah保存余数。如果被除数为32位时&#xff0c;进行除法运算时&#xff0c;ax保存商&#xff0c;d…

动静分离技术

一、HAproxy 动静分离 1、概念&#xff1a; HAproxy 动静分离技术是一种用于优化 Web 服务器性能和提高用户体验的策略&#xff0c;它通过将动态内容和静态内容分别路由到不同的后端服务器来实现&#xff0c;减轻服务器负载&#xff0c;提高网站的响应速度。 动态内容包括由…

NewStarCTF2023week5-隐秘的图片

下载附件解压得到两张图片 第一张二维码扫出来提示没有什么 第二张看到的第一直觉是修复&#xff0c;因为缺了三个定位符&#xff0c;比如下面这种&#xff0c;就是修复定位符&#xff1a; 但是这里这道题仔细看一下&#xff0c;修复好了也不像正常的二维码&#xff0c;并且这…

python自动化测试(七):鼠标事件

前置条件&#xff1a; 本地部署&#xff1a;ECShop的版本是3.0.0、Google版本是 Google Chrome65.0.3325.162 (正式版本) &#xff08;32 位&#xff09; py的selenium版本是3.11.0 目录 一、前置代码 二、ActionChains类 三、鼠标事件 3.1 悬停事件 3.2 左键单击 3…

vivo自研AI大模型即将问世,智能手机行业加速迈向AI时代

当前&#xff0c;以大模型为代表的人工智能技术已发展为新一轮科技革命和产业变革的重要驱动力量&#xff0c;被视作推动经济社会发展的关键增长极。 AI大模型潮起&#xff0c;千行百业走向百舸争流的AI创新应用期&#xff0c;前沿信息技术向手机、PC、车机等消费级终端加速渗…

k8s集群升级

目录 1. 部署cri-docker &#xff08;所有集群节点&#xff09; 2. 升级master节点 3. 升级worker节点 4. 部署containerd 1. 部署cri-docker &#xff08;所有集群节点&#xff09; k8s从1.24版本开始移除了dockershim&#xff0c;所以需要安装cri-docker插件才能使用docker …

代码签名证书如何申请

代码签名证书也称之为软件数字证书&#xff0c;也可以叫作微软信任证书&#xff0c;主要给软件进行身份确定及保护知识产权&#xff0c;也可以被操作系统给信&#xff0c;对于软件开发企业是一项最基本的产品之一。 代码签名证书分为OV和EV两种类型&#xff0c;虽然认证步骤大同…

【网络协议】聊聊TCP如何做到可靠传输的

网络是不可靠的&#xff0c;所以在TCP协议中通过各种算法等机制保证数据传输的可靠性。生活中如何保证消息可靠传输的&#xff0c;那么就是采用一发一收的方式&#xff0c;但是这样其实效率并不高&#xff0c;所以通常采用的是累计确认或者累计应答。 如何实现一个靠谱的协议&…

第 369 场周赛 (3题,递归式动态规划)

第一题 简单题&#xff0c;就不多写了 class Solution:def findKOr(self, nums: List[int], k: int) -> int:ans [0] * 31for n in nums:for i in range(31):if 2**i & n 2**i:ans[i] 1return sum([2**i if ans[i] > k else 0 for i in range(31)])第二题 0 至少…

Azure - 机器学习:使用 Apache Spark 进行交互式数据整理

目录 本文内容先决条件使用 Apache Spark 进行交互式数据整理Azure 机器学习笔记本中的无服务器 Spark 计算从 Azure Data Lake Storage (ADLS) Gen 2 导入和整理数据从 Azure Blob 存储导入和处理数据从 Azure 机器学习数据存储导入和整理数据 关注TechLead&#xff0c;分享AI…

论坛介绍 | COSCon'23 开源文化(GL)

众多开源爱好者翘首期盼的开源盛会&#xff1a;第八届中国开源年会&#xff08;COSCon23&#xff09;将于 10月28-29日在四川成都市高新区菁蓉汇举办。本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相映”&#xff01;各位新老朋友们&#xff0c;欢迎到成都&a…

ZYNQ连载08-Lwip网络组件

ZYNQ连载08-Lwip网络组件 1. 添加Lwip包 2. Lwip配置 我这里关闭ipv6和dhcp。 3. tcp客户端 #include "include/my_tcp.h" #include "lwip/ip.h"#define THREAD_STACKSIZE 1024 #define SERVER_PORT 8000 #define SERVER_ADDR "192.168.3.190&qu…

安全架构的设计理论与实践

安全架构的设计理论与实践 安全架构概述 信息安全面临的威胁 安全架构的定义和范围 信息安全相关的国内外标准及组织 主要安全模型 状态机模型(BLP)模型 Bell-IaPadula模型 Biba模型 Clark-Wilson (CWM)模型 ChineseWall模型 系统安全体系架构规划框架 安全技术体系架构 信息系…

C# 图解教程 第5版 —— 第13章 数组

文章目录 13.1 数组13.1.1 定义13.1.2 重要细节 13.2 数组的类型13.3 数组是对象13.4 一维数组和矩形数组13.5 实例化一维数组或矩形数组13.6 访问数组元素&#xff08;*&#xff09;13.7 初始化数组13.7.1 显示初始化一维数组13.7.2 显示初始化矩形数组13.7.3 初始化矩形数组的…

Python selenium驱动下载,模块安装以及基本使用

视频版教程&#xff1a;一天掌握python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium 我们以谷歌浏览器为例讲解。首先我们要去下载谷歌浏览器驱动。 谷歌浏览器驱动下载地址&#xff1a;Chromium History Versions Download ↓ 查看谷歌浏览器版本 右上角三个点 …

关于测试组件junit切换testng的示例以及切换方式分享

文章目录 概要首先看看junit和testng的区别实践篇摸拟业务逻辑代码简单对象数据层摸拟类业务逻辑层摸拟类后台任务摸拟类 基于springmockjunit基于springmocktestng 示例的差异点junit与testng的主要变动不大,有以下几个点需要注意注解部分在before,after中testng多出按配置执行…

设计模式(16)迭代器模式

一、介绍&#xff1a; 1、定义&#xff1a;迭代器模式 (Iterator Pattern) 是一种行为型设计模式&#xff0c;它提供一种顺序访问聚合对象&#xff08;如列表、集合等&#xff09;中的元素&#xff0c;而无需暴露聚合对象的内部表示。迭代器模式将遍历逻辑封装在一个迭代器对象…