当前位置: 首页 > article >正文

Spring事务和AOP

事务四大特性

原子性:事务不可再分,一组事务只能同时成功或失败

持久性:事务一旦提交就一定会持久化到数据库

隔离性:事务之前不会互相干扰

一致性:数据操作前后,总量保持不变

事务管理

  • 事务管理是应用系统开发中必不可少的一部分,Spring 为事务管理提供了丰富的功能支持
  • Spring 事务管理分为编程式和声明式的两种方式
    • 编程式:是指在写业务代码中将事务代码也写进去,这是很古老的做法了,在现在看起来可能不可思议
    • 声明式:基于AOP将具体业务逻辑与事务处理解耦,声明式事务管理使业务代码逻辑不受污染,因此在实际使用中声明式事务用的比较多
  • 声明式事务有两种方式,一种是在配置文件中做相关的事务规则声明,另一种是基于@Transactional 注解的方式,注解方式是目前主流方式

Transactional注解

  • @Transactional:此注解是 Spring 提供用来控制事务回滚/提交的一个注解,属于声明式事务的实现
  • 作用域:@Transactional可以写在类、接口、方法上
    • 当标注在类上的时候,表示给该类所有的 public 方法添加上 @Transactional注解
    • Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。像 CGLib 动态代理采用继承的方式将会导致 @Transactional 注解失效
    • 当标注在方法上的时候,事务的作用域就只在该方法上生效,并且如果类及方法上都配置 @Transactional注解时,方法的注解会覆盖类上的注解
/**
 * readOnly:只读模式,默认false
 * SUPPORTS:支持模式,被调用时当前方法有事务就加入,没有就不使用
 * REQUIRED(默认):必须要有事务,当前方法有事务就加入,没有就建一个事务
 * NEVER:以非事务方式执行,如果存在事务则抛出异常。
 * REQUIRES_NEW:创建一个新事务,如果存在当前事务,则暂停当前事务
 */
@Service("studentServiceImpl")
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public class StudentServiceImpl implements IStudentService {
    @Autowired
    private StudentMapper studentMapper;


    @Override
    public Student findStudentById(Long id) {
        System.out.println("开始查询");
        return studentMapper.selectById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    //指定发生异常类型回归
    public void saveStudent(Student student) {
        studentMapper.insert(student);
    }

事务传播机制

  • Propagation.REQUIRED:默认,支持当前事务,如果当前没有事务,就创建一个事务,保证一定有事务 -- 增删改方法使用
  • Propagation.SUPPORTS:支持当前事务,如果当前没有事务,就不使用事务 -- 查询方法使用
  • Propagation.REQUIRES_NEW:新建事务,如果当前有事务,就挂起 -- 不常用
  • Propagation.NAVEN:不支持事务,如果当前有事务,就抛出异常 -- 不常用

AOP

概述

Aspect Oriented Programming:简称Aop,意思是面向切面编程,是Spring中的第二大核心,第一大核心是IOC。Aop 是一种编程思想,是面向对象编程(OOP)的一种补充

应用场景

  • 日志记录:自动记录用户的所有操作到数据库中
  • 事务管理:自动在Service层方法前后开启提交事务
  • 权限验证:自动在执行业务代码前执行权限校验
  • 性能监控:自动记录Service层方法执行耗时
  • AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离

代理模式

AOP底层由动态代理实现,分别有JDK动态代理和CGLIB动态代理

静态代理
  • 静态代理是一种代理模式的实现方式,它在编译期间就已经确定了代理对象,需要为每个被代理对象创建一个代理类
  • 静态代理的实现比较简单,但是每个被代理的对象都需要创建一个代理类,因此在代理对象比较多时,会导致代码冗余和维护成本增加
动态代理
  • 动态代理是一种代理模式的实现方式,它在运行期间根据需要动态生成代理对象,无需手动编写代理类,可以减少代码冗余和维护成本
  • 动态代理适用于需要代理的对象数量较多,代理类实现相对灵活的场景,例如Spring中的Aop就是使用的动态代理

AOP实战

1.核心概念

名称

说明

Joinpoint:连接点

连接点指的是可以被Aop控制的方法,例如:入门程序当中所有的Service层方法都是可以被Aop控制的

Pointcut:切入点

切入点指的是哪些类、方法要被拦截,也就是哪些连接点要被拦截,例如:入门程序中切入点就是Service层所有方法

Advice:通知

通知指的是要作用到连接点的功能,例如:入门程序中的四种通知

Target:目标

目标指的是被代理的对象,例如:入门案例中的UserServiceImpl就是目标对象

Aspect:切面

切面指的是切入点和通知的结合,例如:入门案例中的TxManager就被定义为切面

Weaving:织入

织入指的是将增强处理运用到目标对象的过程,例如:在入门案例中事务增强到Service层方法的过程就是织入

Proxy:代理

代理指的是被增强后的对象,也就是织入了增强处理的类,在程序运行时看的到

2.通知分类

通知

说明

before:前置通知

通知方法在目标方法调用之前执行

after:后置通知

通知方法在目标方法执行后执行,核心方法是否异常都会执行

after-returning:返回后通知

通知方法会在目标方法执行后执行,核心方法异常后不执行

after-throwing:抛出异常通知

通知方法会在目标方法抛出异常后执行

around:环绕通知

通知方法会将目标方法封装起来

@Component
@Aspect//定义切面
public class AopConfig {
    @Autowired
    private TxManger txManger;

     * 定义切入点
     *//*

    @Pointcut("execution(* cn.lgc.service.IStudentService.*(..))")
    public void pointcut() {

    }

    
/**
     * 前置通知
     *//*

    */
@Before("pointcut()")
    public void before() {
        txManger.begin();
    }
    
/**
     * 后置通知
     *//*

   */
 @AfterReturning("pointcut()")
    public void afterReturning() {
        txManger.commit();
    }
    
/**
     * 最终通知
     *//*

    */
@After("pointcut()")
    public void after() {
        txManger.close();
    }


    
/**
     * 异常通知
     *//*

    */
@AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowing(Exception e) {
        txManger.rollback();
        System.out.println(e.getMessage());
    }

/**
     * 环绕通知
     *
     * @param pjp
     * @return {@link Object}
     */

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
        	//获取目标方法名
            Signature signature = pjp.getSignature();
            String name = signature.getName();
            if (name.startsWith("find") || name.startsWith("get")) {
                //查询,不开启事务
                Object proceed = pjp.proceed();
                txManger.close();
                return proceed;
            } else {
                //增删改
                txManger.begin();
                Object proceed = pjp.proceed();
                txManger.commit();
                txManger.close();
                return proceed;
            }
        } catch (Throwable e) {
            e.printStackTrace();
            txManger.rollback();
        }
        return null;
    }
}

3.aop实现日志打印

@Component
@Aspect
public class LogConfig {
    //日志持久化对象
    @Autowired
    private LogMapper logMapper;

    //获取当前请求ip
    @Autowired
    private HttpServletRequest request;

    //切入点
    @Pointcut("execution(* cn.lgc.service.IStudentService.*(..))")
    public void pointcut() {

    }

    //通知
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Log log = new Log();
        try {
            Object result = resultWrapper(joinPoint, log);
            logMapper.insert(log);
            //结果传递
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
            System.err.println("日志添加失败");
        } finally {

        }
        return null;
    }

    /**
     * 结果包装器
     *
     * @param joinPoint 加入点
     * @param log       日志
     * @return {@link Object}
     * @throws Throwable
     */
    private Object resultWrapper(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
        //获取目标类完全限定名
        String className = joinPoint.getTarget().getClass().getName();
       //获取方法名
        String methodName = joinPoint.getSignature().getName();
        //获取参数类型
        String args = Arrays.stream(joinPoint.getArgs()).toString();
        long begin = System.currentTimeMillis();
        //执行目标方法
        Object result = joinPoint.proceed();
        long costTime = System.currentTimeMillis() - begin;
        String ip = request.getRemoteAddr();
        //创建持久化对象
        log.setClassName(className);
        log.setMethodName(methodName);
        log.setMethodParams(args);
        log.setUserId(9527L);
        log.setUserName("admin");
        log.setReturnValue(Optional.ofNullable(result).map(m -> m.getClass().getName()).orElse(null));
        log.setCostTime(costTime);
        log.setCreateTime(new Date());
        log.setIp(ip);
        return result;
    }

}

http://www.kler.cn/news/321540.html

相关文章:

  • 文件上传、重定向、Gin路由
  • 融云音视频RTC介绍
  • 六、设计模式-6.1、单例模式
  • 显示技术概念极简理解(分辨率、英寸、PPI、DPI)
  • IDEA Dependency Analyzer 分析 maven 项目包的依赖
  • Python 使用selenium 4.25 进行爬虫(1)
  • 一文读懂电路中VCC、VDD、VEE、VSS的区别
  • YOLOv8改进 - 注意力篇 - 引入SK网络注意力机制
  • 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-26
  • 了解网络的相关信息
  • 从0开始linux(5)——vim
  • 微信小程序-canvas
  • go语言网络编程
  • 【Linux 从基础到进阶】Kafka消息队列配置与管理
  • C/C++中的内存管理
  • c语言200例 063 信息查询
  • 数据结构 ——— 移除元素(快慢指针)
  • io流(学习笔记03)字符集
  • 大数据时代的PDF解析:技术与挑战
  • Python:百度贴吧实现自动化签到
  • Spring是什么
  • 有源蜂鸣器(5V STM32)
  • 无人机之虚拟云台技术篇
  • LeetCode 137. 只出现一次的数字 II
  • Linux安装vim超详细教程
  • MySQL重点,面试题
  • 深入Android UI开发:从自定义View到高级布局技巧的全面学习资料
  • RestSharp简介
  • 通信工程学习:什么是SDN软件定义网络
  • 电脑如何设置代理IP:详细步骤指南