Javaweb-day13事务管理AOP

spring的事务管理&spring框架的第二大核心AOP面向切面编程

spring框架的第一大核心是前面讲的IOC控制反转

事务管理

事务回顾

概念:事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。

Spring事务管理

@Transactional作用:就是在当前这个方法执行开始之前来开启事务,方法执行完毕之后提交事务。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作。

@Transactional注解:我们一般会在业务层当中来控制事务,因为在业务层当中,一个业务功能可能会包含多个数据访问的操作。在业务层来控制事务,我们就可以将多个数据访问操作控制在一个事务范围内。

@Transactional注解书写位置:

  • 方法

    • 当前方法交给spring进行事务管理

    • 当前类中所有的方法都交由spring进行事务管理

  • 接口

    • 接口下所有的实现类当中所有的方法都交给spring 进行事务管理

一般选择加在业务层的增删改这一类方法上,准确来说,是加在业务层执行多次数据访问操作增删改这一类方法上

无需控制事务: 查询操作,一步简单的增删改操作(mysql数据库的事务是自动提交的

可以在application.yml配置文件中开启事务管理日志,这样就可以在控制看到和事务相关的日志信息了

事务进阶

rollbackFor

异常回滚的属性

默认情况下,只有出现RuntimeException(运行时异常)才会回滚事务。

假如我们想让所有的异常都回滚,需要来配置@Transactional注解当中的rollbackFor属性,通过rollbackFor这个属性可以指定出现何种异常类型回滚事务。

propagation

事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

AOP基础

概述

AOP英文全称:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。

而AOP面向方法编程,就可以做到在不改动这些原始方法的基础上,针对特定的方法进行功能的增强。

AOP的作用:在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性: 解耦)

不论运行的是那个业务方法,最后其实运行的就是我们定义的模板方法,而在模板方法中,就完成了原始方法执行耗时的统计操作 。(那这样呢,我们就通过一个模板方法就完成了指定的一个或多个业务方法执行耗时的统计)

这个流程,似曾相识,和我们之前所学习的动态代理技术是非常类似的。 我们所说的模板方法,其实就是代理对象中所定义的方法,那代理对象中的方法以及根据对应的业务需要, 完成了对应的业务功能,当运行原始业务方法时,就会运行代理对象中的方法,从而实现统计业务方法执行耗时的操作。

其实,AOP面向切面编程和OOP面向对象编程一样,它们都仅仅是一种编程思想,而动态代理技术是这种思想最主流的实现方式。而Spring的AOP是Spring框架的高级技术,旨在管理bean对象的过程中底层使用动态代理机制,对特定的方法进行编程(功能增强)。

动态代理技术是Java中一种非常强大的设计模式,它允许在运行时创建一个实现了一组给定接口的新类。动态代理的主要用途是在不修改原始对象的情况下,对方法调用进行增强或拦截。这种技术在AOP(面向切面编程)、事务管理、权限控制等方面有广泛的应用。

动态代理的基本概念
  1. 代理对象:动态生成的代理对象实现了与目标对象相同的接口。
  2. InvocationHandler:代理对象在调用方法时,会委托给一个实现了 InvocationHandler 接口的对象来处理。
  3. Proxy 类:Java 提供了一个 Proxy 类,用于创建动态代理对象。
工作原理
  1. 创建接口:定义一个或多个接口,这些接口将由目标对象和代理对象共同实现。
  2. 实现 InvocationHandler:创建一个实现了 InvocationHandler 接口的类,重写 invoke 方法。
  3. 创建代理对象:使用 Proxy.newProxyInstance 方法创建代理对象。
示例代码

假设我们有一个简单的接口 UserService 和一个实现类 UserServiceImpl

1. 定义接口
public interface UserService {
    void addUser(String user);
    void deleteUser(String user);
}
2. 实现接口
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String user) {
        System.out.println("Adding user: " + user);
    }

    @Override
    public void deleteUser(String user) {
        System.out.println("Deleting user: " + user);
    }
}
3. 实现 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserServiceInvocationHandler implements InvocationHandler {
    private final UserService userService;

    public UserServiceInvocationHandler(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(userService, args);
        System.out.println("After method: " + method.getName());
        return result;
    }
}
4. 创建代理对象
public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();

        // 创建 InvocationHandler
        UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService);

        // 创建代理对象
        UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),
                new Class[]{UserService.class},
                handler
        );

        // 调用代理对象的方法
        proxyUserService.addUser("Alice");
        proxyUserService.deleteUser("Bob");
    }
}
输出
Before method: addUser
Adding user: Alice
After method: addUser
Before method: deleteUser
Deleting user: Bob
After method: deleteUser
关键点解释
  1. Proxy.newProxyInstance

    • loader:类加载器,通常使用目标对象的类加载器。
    • interfaces:代理对象需要实现的接口列表。
    • hInvocationHandler 实例,用于处理方法调用。
  2. InvocationHandler.invoke

    • proxy:代理对象。
    • method:被调用的方法。
    • args:方法参数。
动态代理的优势
  1. 灵活性:可以在运行时动态生成代理对象,无需在编译时确定。
  2. 扩展性:可以通过代理对象轻松添加新的功能,而不需要修改原始对象。
  3. 解耦:原始对象和代理对象之间解耦,使得代码更加模块化和可维护。
应用场景
  1. AOP(面向切面编程):在方法调用前后添加日志记录、事务管理等功能。
  2. 权限控制:在方法调用前检查用户权限。
  3. 缓存:在方法调用前检查缓存,避免重复计算。
  4. 性能监控:记录方法调用的时间,用于性能分析。

通过动态代理技术,可以灵活地扩展和增强现有功能,而无需修改原始代码,使得系统更加健壮和可维护。

模板方法当中定义的逻辑其实就是创建出来的代理对象方法中的逻辑

SpringAOP的底层就是通过动态代理技术完成针对于特定方法的编程

动态代理技术回头可能要补一下【立马补别拖】

AOP的优势:

  1. 减少重复代码

  2. 提高开发效率

  3. 维护方便

动态代理特点:无侵入式的给代码增加额外的功能

快速入门

@Component
@Aspect //当前类为切面类,即AOP类
@Slf4j
public class TimeAspect {

    @Around("execution(* com.itheima.service.*.*(..))") //切入点表达式
    public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
        //记录方法执行开始时间
        long begin = System.currentTimeMillis();

        //执行原始方法
        Object result = pjp.proceed();

        //记录方法执行结束时间
        long end = System.currentTimeMillis();

        //计算方法执行耗时
        log.info(pjp.getSignature()+"执行耗时: {}毫秒",end-begin);

        return result;
    }
}

  • 事务管理:我们前面所讲解的Spring事务管理,底层其实也是通过AOP来实现的,只要添加@Transactional注解之后,AOP程序自动会在原始方法运行前先来开启事务,在原始方法运行完毕之后提交或回滚事务

没跟、后面项目实际运用到再写

面向特定的方法进行编程,可以在不改动原始方法的基础上,针对于原始的方法进行编程

动态代理技术是面向切面编程这种思想最主流的实现方式

SpringAOP底层就是基于动态代理技术针对于特定的方法进行编程

核心概念

1. 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

连接点指的是可以被aop控制的方法。例如:入门程序当中所有的业务方法都是可以被aop控制的方法。

在SpringAOP提供的JoinPoint当中,封装了连接点方法在执行时的相关信息。

2. 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)

在入门程序中是需要统计各个业务方法的执行耗时的,此时我们就需要在这些业务方法运行开始之前,先记录这个方法运行的开始时间,在每一个业务方法运行结束的时候,再来记录这个方法运行的结束时间。

但是在AOP面向切面编程当中,我们只需要将这部分重复的代码逻辑抽取出来单独定义。抽取出来的这一部分重复的逻辑,也就是共性的功能。

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

在通知当中,我们所定义的共性功能到底要应用在哪些方法上?此时就涉及到了切入点pointcut概念。切入点指的是匹配连接点的条件。通知仅会在切入点方法运行时才会被应用。

在aop的开发当中,我们通常会通过一个切入点表达式来描述切入点(后面会有详解)。

4. 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)

当通知和切入点结合在一起,就形成了一个切面。通过切面就能够描述当前aop程序需要针对于哪个原始方法,在什么时候执行什么样的操作。

切面所在的类,我们一般称为切面类(被@Aspect注解标识的类)

5. 目标对象:Target,通知所应用的对象

目标对象指的就是通知所应用的对象,我们就称之为目标对象。

无论你加不加aop注解,他都会创建一个代理,代理执行的方法和原来的方法内容一样,如果你加了aop注解,那么他会把你写的方法覆盖掉他的代理方法,执行

Spring的AOP底层是基于动态代理技术来实现的,也就是说在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强。

AOP进阶

通知类型

通知顺序

多个切面类当中的通知执行顺序

同一个切面类当中不同类型的通知执行顺序后面自己在研究

不用死记硬背,以项目的实际运行效果为准

切入点表达式

切入点表达式的语法规则:

  1. 方法的访问修饰符可以省略

  2. 返回值可以使用*号代替(任意返回值类型)

  3. 包名可以使用*号代替,代表任意包(一层包使用一个*

  4. 使用..配置包名,标识此包以及此包下的所有子包

  5. 类名可以使用*号代替,标识任意类

  6. 方法名可以使用*号代替,表示任意方法

  7. 可以使用 * 配置参数,一个任意类型的参数

  8. 可以使用.. 配置参数,任意个任意类型的参数

切入点表达式示例

  • 省略方法的修饰符号

    execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
  • 使用*代替返回值类型

    execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
  • 使用*代替包名(一层包使用一个*

    execution(* com.itheima.*.*.DeptServiceImpl.delete(java.lang.Integer))
  • 使用..省略包名

    execution(* com..DeptServiceImpl.delete(java.lang.Integer))    
  • 使用*代替类名

    execution(* com..*.delete(java.lang.Integer))   
  • 使用*代替方法名

    execution(* com..*.*(java.lang.Integer))   
  • 使用 * 代替参数

    execution(* com.itheima.service.impl.DeptServiceImpl.delete(*))
  • 使用..省略参数

    execution(* com..*.*(..))
    
    
    根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。execution(* com.itheima.service.DeptService.list(..)) || execution(* com.itheima.service.DeptService.delete(..))

实现步骤:

  1. 编写自定义注解

  2. 在业务类要做为连接点的方法上添加自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
  • execution切入点表达式

    • 根据我们所指定的方法的描述信息来匹配切入点方法,这种方式也是最为常用的一种方式

    • 如果我们要匹配的切入点方法的方法名不规则,或者有一些比较特殊的需求,通过execution切入点表达式描述比较繁琐

  • annotation 切入点表达式

    • 基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以了

连接点

@Slf4j
@Component
@Aspect
public class MyAspect7 {

    @Pointcut("@annotation(com.itheima.anno.MyLog)")
    private void pt(){}
   
    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect7 -> before ...");
    }
    
    //后置通知
    @Before("pt()")
    public void after(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect7 -> after ...");
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //获取目标类名
        String name = pjp.getTarget().getClass().getName();
        log.info("目标类名:{}",name);

        //目标方法名
        String methodName = pjp.getSignature().getName();
        log.info("目标方法名:{}",methodName);

        //获取方法执行时需要的参数
        Object[] args = pjp.getArgs();
        log.info("目标方法参数:{}", Arrays.toString(args));

        //执行原始方法
        Object returnValue = pjp.proceed();

        return returnValue;
    }
}

AOP案例

  • 准备工作

    1. 引入AOP的起步依赖

    2. 导入资料中准备好的数据库表结构,并引入对应的实体类

  • 编码实现

    1. 自定义注解@Log

    2. 定义切面类,完成记录操作日志的逻辑

代码实现细节: 获取request对象,从请求头中获取到jwt令牌,解析令牌获取出当前用户的id。

package com.wujiao.aop;

import com.alibaba.fastjson.JSONObject;
import com.wujiao.mapper.OperateLogMapper;
import com.wujiao.pojo.OperateLog;
import com.wujiao.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;

@Slf4j
@Component
@Aspect
public class LogAspect {
    @Autowired
    private HttpServletRequest request;

    @Autowired
    private OperateLogMapper operateLogMapper;

    @Around("@annotation(com.wujiao.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;
    }
}

也算是把昨天欠的债还上了

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

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

相关文章

JavaWeb后端开发知识储备1

目录 1.DTO/VO/PO 2.MVC架构/微服务架构 3.JWT令牌流程 4.ThreadLocal 5.接口路径/路径参数 6.自定义注解 1.DTO/VO/PO 1.1 DTO DTO 即 Data Transfer Object—— 数据传输对象,是用于传输数据的对象,通常在服务层与表现层之间传递数据&#xff…

BLE 蓝牙客户端和服务器连接

蓝牙通信在设计小型智能设备时非常普遍,之前一直没有使用过,最近使用ardunio ESP32 做了一些实验,做了一个收听播客的智能旋钮(Smart Knob),它带有一个旋转编码器和两个按键。 本文介绍BLE 服务器Server和W…

海康威视和大华视频设备对接方案

目录 一、海康威视 【老版本】 【新版本】 二、大华 一、海康威视 【老版本】 URL规定: rtsp://username:password[ipaddress]/[videotype]/ch[number]/[streamtype] 注:VLC可以支持解析URL里的用户名密码,实际发给设备的RTSP请求不支…

STM32设计智能翻译手势识别加算法系统

目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 电路图采用Altium Designer进行设计: 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 在全球化的浪潮下,语言的多样性也为人们的交流带来了不小的挑战…

基本定时器---内/外部时钟中断

一、定时器的概念 定时器(TIM),可以对输入的时钟信号进行计数,并在计数值达到设定值的时候触发中断。 STM32的定时器系统有一个最为重要的结构是时基单元,它由一个16位计数器,预分频器,和自动重…

Qt文件目录操作

文件目录操作相关类 Qt 为文件和目录操作提供了一些类,利用这些类可以方便地实现一些操作。Qt 提供的与文件和目录操作相关的类包括以下几个: QCoreApplication:用于提取应用程序路径,程序名等文件信息;QFile&#x…

.NET 通过模块和驱动收集本地EDR的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

css中的box-sizing,记录

border-box:最终高度为height,默认包含padding border等属性 content-box:box-sizing默认值,最终大小为heightpaddingborder 等

【AI绘画】Alpha-VLLM 的 Lumina-Next:新一代图像生成器

简介 Lumina-Next-T2I 是在 Lumina-T2I 成功基础上发展起来的尖端图像生成模型。它采用了带有 2B 参数模型的 Next-DiT 和 Gemma-2B 文本编码器,推理速度更快,生成样式更丰富,并增强了多语言支持。 模型架构 Lumina-Next-T2I 的生成模型建…

Redis学习 ——缓存

文章目录 一、Redis缓存的介绍二、Redis缓存问题2.1 缓存穿透2.2 缓存击穿2.3 缓存雪崩2.4 双写一致性2.5 缓存持久化RDBAOF 三、缓存数据管理3.1 数据过期策略3.2 数据淘汰策略 一、Redis缓存的介绍 我们在日常的代码编写中比较少使用到Redis,但是如果涉及到了比较…

【阅读记录-章节2】Build a Large Language Model (From Scratch)

目录 2.Working with text data2.1 Understanding word embeddings2.2 Tokenizing text通过一个简单的实验来理解文本的词元化概念关键概念 2.3 Converting tokens into token IDs实现分词器类(Tokenizer Class)应用分词器测试文本的编码与解码通过分词器…

etcd部署(基于v3.5.15)

etcd部署 单节点部署下载etcd,解压etcd二进制包,并进入解压后目录创建数据目录移动可执行文件到/usr/local/bin/目录测试版本配置systemd管理启动etcd,设置开机启动验证 集群部署(3节点)环境准备准备3台服务器配置3台服务器hosts配置3台服务器…

HTML5实现趣味飞船捡金币小游戏(附源码)

文章目录 1.设计来源1.1 主界面1.2 游戏中界面1.2 飞船边界框效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/143799554 HTML5实现趣味飞船捡金币小游戏(附源码)&…

ASP.NET Core Webapi 返回数据的三种方式

ASP.NET Core为Web API控制器方法返回类型提供了如下几个选择&#xff1a; Specific type IActionResult ActionResult<T> 1. 返回指定类型&#xff08;Specific type&#xff09; 最简单的API会返回原生的或者复杂的数据类型&#xff08;比如&#xff0c;string 或者…

汽车资讯新动力:Spring Boot技术驱动

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

Go语言跨平台桌面应用开发新纪元:LCL、CEF与Webview全解析

开篇寄语 在Go语言的广阔生态中&#xff0c;桌面应用开发一直是一个备受关注的领域。今天&#xff0c;我将为大家介绍三款基于Go语言的跨平台桌面应用开发框架——LCL、CEF与Webview&#xff0c;它们分别拥有独特的魅力和广泛的应用场景。通过这三款框架&#xff0c;你将能够轻…

如何确保爬取的数据准确性和完整性?

在数据驱动的业务环境中&#xff0c;爬虫程序的准确性和完整性至关重要。本文将探讨如何使用Java编写爬虫程序&#xff0c;并确保其在爬取数据时的准确性和完整性。 1. 精确的HTML解析 确保数据准确性的第一步是精确地解析HTML。Jsoup是Java中常用的HTML解析库&#xff0c;它提…

【linux】如何扩展磁盘容量(VMware虚拟机)-转载

如何扩展磁盘容量(VMware虚拟机) 一、前置准备工作 扩展虚拟机磁盘前&#xff0c;需要先把虚拟机关机才能进行扩展磁盘操作 1.选择虚拟机设置&#xff0c;如下图所示 2.输入你想扩展的磁盘容量&#xff0c;以本次实操为例&#xff0c;我这里输入的30G&#xff08;具体按照实…

esp32学习:windows下idf离线安装(最简单)

很多朋友学习esp32时发现&#xff0c;安装idf很麻烦&#xff0c;需要很多依赖&#xff0c;那有没有简单的方法呢&#xff0c;答案是肯定的. 乐鑫Windows 平台工具链的标准设置 - ESP32-S3 - — ESP-IDF 编程指南 release-v5.0 文档编程指南里提供了windows下离线安装包 dl.espr…

tensorflow案例6--基于VGG16的猫狗识别(准确率99.8%+),以及tqdm、train_on_batch的简介

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 本次还是学习API和如何搭建神经网络为主&#xff0c;这一次用VGG16去对猫狗分类&#xff0c;效果还是很好的&#xff0c;达到了99.8% 文章目录 1、tqdm…