SSM框架学习记录-Spring_day03

news2024/11/21 0:36:32

1.AOP简介

代码参考Spring_17_aop_demo


什么是AOP?

AOP(Aspect Oriented Programming)即面向切面编程,一种编程范式,指导开发者如何组织程序结构


AOP作用

BookDaoImpl.java中,执行save方法显然可以计算程序执行时间,但实际上运行updatedelete方法也可以完成相同功能,这是为什么?通过AOP实现,即在不惊动原始设计的基础上为其进行功能增强


@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        // 记录程序当前执行执行(开始时间)
        Long startTime = System.currentTimeMillis();
        // 业务执行万次
        for (int i = 0;i<10000;i++) {
            System.out.println("book dao save ...");
        }
        // 记录程序当前执行时间(结束时间)
        Long endTime = System.currentTimeMillis();
        // 计算时间差
        Long totalTime = endTime-startTime;
        // 输出信息
        System.out.println("执行万次消耗时间:" + totalTime + "ms");
    }

    public void update(){
        System.out.println("book dao update ...");
    }

    public void delete(){
        System.out.println("book dao delete ...");
    }

    public void select(){
        System.out.println("book dao select ...");
    }
}

AOP核心概念

其实在上述方法中select方法运行后并不会实现相同功能,为什么会被区别对待?这就需要先了解AOP的一些核心概念


  • 连接点:类中可以被增强的方法(广义上是程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等),像selectupdatedelete都属于连接点(切入点一定是连接点,但连接点不一定要被增强,所以可能不是切入点)
  • 切入点:类中实际需要被增强的方法(一个或多个),可以通过匹配符去匹配:
    • 一个具体的方法:如com.psj.dao包下的BookDao接口中的无形参无返回值的save方法
    • 匹配多个方法:所有的get开头的方法等
  • 通知:存放共性功能的方法,即要增强的内容,比如MyAdvice类中的method方法就是通知
  • 切面:通知和切入点都可以有多个,哪个切入点需要添加哪个通知,需要切面进行它们之间的关系描述
  • 通知类:通知是方法,用于定义该方法的类就是通知类

在这里插入图片描述

  • 目标对象:原始功能除去共性功能后产生的对象,它无法完成最终工作
  • 代理:目标对象要完成工作,则需要通过原始对象的代理对象实现

2.AOP入门案例

如何在方法执行前输出当前系统时间?

代码参考Spring_18_aop_quickstart


导入坐标

  • 因为spring-context中已经导入了spring-aop ,所以无需再单独导入spring-aop
  • AspectJAOP思想的一个具体实现,Spring有自己的AOP实现,但比AspectJ麻烦

制作连接点

即代码中的BookDaoImpl.java


@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println("book dao save ...");
    }

    public void update(){
        System.out.println("book dao update ...");
    }
}

制作共性功能

即写通知类与通知


public class MyAdvice {
    public void method(){  // 定义共性功能
    	System.out.println(System.currentTimeMillis());
    }
}

定义切入点

假设只想增强BookDaoImpl类下的update方法,需要定义切入点,而切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑


public class MyAdvice {
    @Pointcut("execution(void com.psj.dao.BookDao.update())")  // 定义在哪执行
    private void pt(){}  // 不具有实际意义
    
    public void method(){
    	System.out.println(System.currentTimeMillis());
    }
}

制作切面

即绑定切入点与通知之间的关系


public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())") 
    private void pt(){}
    
    // @Before即表示通知会在切入点方法执行之前执行
    @Before("pt()")  // 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
    public void method(){
    	System.out.println(System.currentTimeMillis());
    }
}

将通知类配给容器并标识其为切面类


@Component  // 给Spring容器管理
@Aspect  // 表示为切面类
public class MyAdvice {
    @Pointcut("execution(void com.psj.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
    	System.out.println(System.currentTimeMillis());
	}
}

开启注解格式AOP功能


@Configuration
@ComponentScan("com.psj")
@EnableAspectJAutoProxy  // 告诉Spring有用注解开发的AOP
public class SpringConfig {
}

3.AOP工作流程


  • Spring容器启动:此时bean对象未被创建
  • 读取所有切面配置中的切入点:并不是读取所有切入点,因为有些定义了但是未使用
public class MyAdvice {
    @Pointcut("execution(void com.psj.dao.BookDao.update())")
    private void pt(){}
    
    @Pointcut("execution(void com.psj.dao.BookDao.select())")
    private void pt1(){}  // 该切入点未被使用,所以不会被读取
    
    @Before("pt()")  // 只使用了pt
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
  • 初始化bean:即判定bean对应的类中的方法是否匹配到任意切入点

    • 匹配失败:说明不需要增强,直接调用原始对象的方法即可,比如UserDao中的方法就无法匹配到pt切入点,容器中的对象是目标对象本身

    • 匹配成功:创建原始对象(目标对象)的代理对象,容器中的对象是目标对象的代理对象(要验证是否为代理对象,可以打印当前对象的getClass方法)

4.AOP配置管理


AOP切入点表达式

切入点是要进行增强的方法,而切入点表达式是要进行增强的方法的描述方式

代码参考Spring_19_aop_pointcut


  • 语法格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
// 两种都是可以的,因为调用接口方法时最终运行的还是其实现类的方法
execution(void com.psj.dao.BookDao.update())
execution(void com.psj.dao.impl.BookDaoImpl.update())
  • 通配符:

    • *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
    // 匹配com.psj包下的任意包中的UserService类/接口中所有find开头的带有一个参数的方法
    execution(public * com.psj.*.UserService.find*(*))
    
    • ..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
    // 匹配com包下的任意包中的UserService类/接口中所有名称为findById的方法
    execution(public User com..UserService.findById(..))
    
    • +:专用于匹配子类类型,很少使用
    // *Service+表示所有以Service结尾的接口的子类。
    execution(* *..*Service+.*(..))
    
  • 书写技巧:

    • 描述切入点通常描述接口(如果描述到实现类会出现紧耦合)
    • 访问控制修饰符针对接口开发均采用public描述(可省略)
    • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
    • 包名书写尽量不使用..匹配(效率过低),常用*
    • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service
    • 方法名书写以动词进行精准匹配,名词采用匹配,例如getById书写成getBy*
    • 通常不使用异常作为匹配规则

AOP通知类型

通知描述了抽取的共性功能,根据共性功能抽取的位置不同,运行代码时要将其加入到合理的位置

代码参考Spring_20_aop_advice_type


  • 前置通知(before):追加功能到方法执行前

  • 后置通知(after):追加功能到方法执行后,不管方法执行的过程中有没有抛出异常都会执行

  • 返回后通知(afterReturning):追加功能到方法执行后,只有方法正常执行结束后才进行(如果方法执行抛出异常,返回后通知将不会被添加)

  • 抛出异常后通知(afterThrowing):追加功能到方法抛出异常后,只有方法执行出异常才进行

  • 环绕通知(Around):可以追加功能到方法执行的前后(可以不使用ProceedingJoinPoint调用原始方法),它可以实现其他四种通知类型的功能

    • 原始方法不具有返回值:通知方法的返回值类型可以设置成void,也可以设置成Object
    @Around("pt()")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        // 表示对原始操作的调用
        pjp.proceed();  
        System.out.println("around after advice ...");
        return ret;
    }
    
    • 原始方法具有返回值:可以不接收返回值,通知方法设置成void即可,如果接收返回值,最好设定为Object类型
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {  // 需要有原始方法的返回值
        System.out.println("around before advice ...");
        // 表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;  // 执行原始方法后需要将其返回值返回(当然可以自定义返回值)
    }
    

在这里插入图片描述

小Demo-业务层接口执行效率

完成一个需求:任意业务层接口执行均可显示其执行效率(执行时长)

代码参考Spring_21_case_interface_run_speed


AOP通知获取数据

AOP是在原始方法前后追加操作,所以获取数据即获取原始方法中的数据

代码参考Spring_22_aop_advice_data


  • 获取切入点参数:所有的通知类型都可以获取参数

    • JoinPoint:适用于前置、后置、返回后、抛出异常后通知
    • ProceedingJoinPoint适用于环绕通知
  • 获取切入点方法返回值:前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究

    • 返回后通知
    • 环绕通知
  • 获取切入点方法运行异常信息:前置和返回后通知不会有,后置通知可有可无,所以不做研究

    • 抛出异常后通知
    • 环绕通知
@Pointcut("execution(* com.psj.dao.BookDao.findName(..))")
private void pt() {}

// JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
@Before("pt()")
public void before(JoinPoint jp) {
    Object[] args = jp.getArgs();
    System.out.println(Arrays.toString(args));
    System.out.println("before advice ...");
}

@After("pt()")
public void after(JoinPoint jp) {
    Object[] args = jp.getArgs();
    System.out.println(Arrays.toString(args));
    System.out.println("after advice ...");
}

// ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
    Object[] args = pjp.getArgs();
    System.out.println(Arrays.toString(args));
    args[0] = 666;  // 可以先进行处理再传给切入点
    Object ret = null;
    try {
        ret = pjp.proceed(args);  // 调用无参数的proceed,当原始方法有参数,会在调用的过程中自动传入参数
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return ret;
}

// 设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()", returning = "ret")
public void afterReturning(JoinPoint jp, String ret) {
    System.out.println("afterReturning advice ..." + ret);
}

// 设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
@AfterThrowing(value = "pt()", throwing = "t")
public void afterThrowing(Throwable t) {
    System.out.println("afterThrowing advice ..." + t);
}

小Demo-百度网盘密码数据兼容处理

删除百度网盘分享链接输入密码时尾部多输入的空格,处理方式:

  • 在业务方法执行之前对所有的输入参数进行格式处理(使用trim方法)
  • 使用处理后的参数调用原始方法(环绕通知中存在对原始方法的调用)

代码参考Spring_23_case_handle_password


@Around("DataAdvice.servicePt()")
public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
    Object[] args = pjp.getArgs();
    // 对参数进行预处理
    for (int i = 0; i < args.length; i++) {
        // 判断参数是不是字符串
        if(args[i].getClass().equals(String.class)){
            args[i] = args[i].toString().trim();
        }
    }
    // 传入预处理的参数
    Object ret = pjp.proceed(args);
    return ret;
}

5.AOP事务管理


Spring事务简介

  • 事务作用:在数据层保障一系列的数据库操作同成功同失败
  • Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败(以转账为例,减钱和加钱分别调用一次数据层,意味着出现两个事务,这没法保证存钱和取钱同时成功或失败,所以需要把事务放在业务层处理)

转账案例

实现任意两个账户间转账操作:

  • 数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)
  • 业务层提供转账操作(transfer),调用减钱与加钱的操作
  • 提供2个账号和操作金额执行转账操作

代码参考Spring_24_case_transfer


  • 在业务接口上添加Spring事务管理:@Transactional可以加在业务层接口上方、业务层实现类上方以及业务方法上方
public interface AccountService {
    // 配置当前接口方法具有事务(该注解一般写在接口上)
    @Transactional
    public void transfer(String out, String in, Double money);
}
  • 设置事务管理器:
public class JdbcConfig {
    // 其余代码省略
    // 配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

Spring事务角色

Spring不是数据库,为什么可以进行事务管理?通过事务管理员和协调员

  • 未开启事务:AccountDao接口中的outMoney方法和inMoney方法修改操作,各自会开启事务T1T2,此时AccountService类中的transfer方法没有事务,所以如果其中一个事务出现异常就会导致数据错误
  • 开启事务:在transfer方法上添加了@Transactional注解后会有事务TT1T2会加入到T中,这就保证它们在同一个事务中,当业务层中出现异常,整个事务就会回滚,保证数据的准确性

目前的事务管理基于DataSourceTransactionManagerSqlSessionFactoryBean使用的是同一个数据源


  • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法(transfer)
  • 事务协调员:加入事务方,在Spring中通常指代数据层方法(outMoneyinMoney),也可以是业务层方法

Spring事务属性


  • 事务配置:下面属性都可在@Transactional注解的参数上进行设置(Spring的事务只会对Error异常和RuntimeException异常及其子类进行事务回滚,其他的异常类型可不回滚,比如IOException,所以可设置@Transactional(rollbackFor = {IOException.class}))

在这里插入图片描述

  • 转账业务追加日志案例(代码参考Spring_25_case_transfer_log):在前面的转案例的基础上添加新的需求,完成转账后记录日志
  • 基于转账操作案例添加日志模块,实现数据库中记录日志
  • 业务层转账操作去调用减钱、加钱与记录日志功能
  • 无论转账操作是否成功,均进行转账操作的日志留痕
// 对应接口还是添加了@Transactional注解的
public void transfer(String out,String in ,Double money) {
    try{
        accountDao.outMoney(out,money);
        int i = 1/0;
        accountDao.inMoney(in,money);
    }finally {
        logService.log(out,in,money);  // 保证代码运行
    }
}
  • 事务传播行为:即事务协调员对事务管理员所携带事务的处理态度(相当于T3选择加入到T,还是log方法自己成为新事务)

    对于loginMoneyoutMoney都属于增删改,分别有事务T1T2T3。前面提到Spring事务会把三个事务加入到事务T(即transfer开启的事务)中,当转账失败后,所有的事务都回滚,导致日志没有记录下来,能不能让log方法单独是一个事务呢?需用到事务传播行为

public interface LogService {
    // propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}

在这里插入图片描述

参考


https://www.bilibili.com/video/BV1Fi4y1S7ix?p=31-42

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

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

相关文章

Redis框架(三):大众点评项目 基于Session的短信登录

大众点评项目 基于Session的短信登录需求&#xff1a;基于Session实现短信验证登录基于Session的短信登录发送手机验证码实现登录 &#xff08;注意MyBatisP的接口使用&#xff09;新的问题SpringCloud章节复习已经过去&#xff0c;新的章节Redis开始了&#xff0c;这个章节中将…

C#语言实例源码系列-实现无损压缩图片

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

微电网两阶段鲁棒优化问题(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Redis之相关介绍、远程docker部署以及相关shell命令

Redis相关shell命令一、概述1、介绍2、作用3、特性4、官方网址二、远程服务Docker上Redis相关测试及命令1、Redis安装及挂载1.1 查找所有关于Redis1.2 拉取最高版本的Redis1.3 通过xftp连接到远程服务器1.4 挂载1.5 开启远程服务器的端口1.6 修改配置文件2、开始使用Redis2.1 开…

autoconf-archive源码安装

0. 源码地址 autoconf-archive源码下载地址经由https://savannah.gnu.org搜索"autoconf-archive"到GNU Autoconf Archive - Summary [Savannah] 再在其中点击上图中箭头位置&#xff0c;转到GitHub - autoconf-archive/autoconf-archive: A mirror of the GNU Autoc…

数据分析软件-FineReport内置SQl提交

1. 概述 1.1 版本 报表服务器版本 功能变动 11.0.2 填报配置表时支持从数据库中模糊搜索表&#xff0c;详情见 2.2 节。 1.3 功能介绍 设计好填报表格&#xff0c;添加填报控件之后&#xff0c;如下图所示&#xff1a; 需要将填报数据的单元格与数据库表字段进行绑定&#…

【微服务】2、一篇文章详解 Ribbon 负载均衡

Ribbon 负载均衡一、负载均衡原理&#xff08;debug 源码&#xff09;(1) 基本介绍(2) 打断点① LoadBalancerInterceptor.java - intercept()② RibbonLoadBalancerClient.java - execute()③ RibbonLoadBalancerClient.java - execute()④ RibbonLoadBalancerClient.java - g…

【STM32】详解RTC实时时钟的概念和配置示例代码

一、什么是RTC RTC(Real-time Clock)&#xff1a;实时时钟&#xff0c;本质上是一个支持BCD编码的定时器/计数器。主电源断电后能够由电池供电&#xff0c;使其时钟跳转依然正常。 二、STM32F4芯片内的RTC功能 ①日历时钟&#xff08;时分秒、年月日、星期&#xff09; ②两个闹…

六、排序算法介绍3

4、希尔排序 4.1 简单插入排序问题 简单的插入排序可能存在的问题&#xff0c;数组 arr { 2, 3, 4, 5, 6, 1 } 这时需要插入的数 1(最小)&#xff0c;简单插入排序的过程如下&#xff1a; {2,3,4,5,6,6} {2,3,4,5,5,6} {2,3,4,4,5,6} {2,3,3,4,5,6} {2,2,3,4,5,6} {1,2,3,4,…

CCIA技术沙龙 | “数据安全风险评估及安全服务实践” 沙龙成功举办

2022年12月8日&#xff0c;由中国网络安全产业联盟&#xff08;CCIA&#xff09;主办、CCIA数据安全工作委员会支持、杭州美创科技股份有限公司承办的“数据安全风险评估及数据安全服务实践”主题技术沙龙成功举办。 当前&#xff0c;我国数字经济快速发展、数字化转型持续深入…

Java对象深拷贝详解(List深拷贝)

1、Java中拷贝的概念 在Java语言中&#xff0c;拷贝一个对象时&#xff0c;有浅拷贝与深拷贝两种 浅拷贝&#xff1a;只拷贝源对象的地址&#xff0c;所以新对象与老对象共用一个地址&#xff0c;当该地址变化时&#xff0c;两个对象也会随之改变。 深拷贝&#xff1a;拷贝对…

一起学习用Verilog在FPGA上实现CNN----(一)总体概述

1 总体概述 为避免闭门造车&#xff0c;找一个不错的开源项目&#xff0c;学习在FPGA上实现CNN&#xff0c;为后续的开发奠定基础 1.1 项目链接 大佬的开源项目链接&#xff1a; CNN-FPGA 链接跳转界面如下&#xff1a; 大佬的该项目已经发表论文&#xff0c;而且开源工程结…

Qt5.6.1移植海思Hi3521d(一)

系列文章目录 文章目录系列文章目录前言一、开发环境二、搭建环境1.准备2.海思SDK和交叉编译器安装2.测试交叉编译器一下3.安装tftp总结前言 上半年做个一个Qt移植海思芯片的程序&#xff0c;感觉差不多快忘记了&#xff0c;赶紧记录一下 一、开发环境 系统&#xff1a;Ubunt…

初学Python到月入过万最快的兼职途径(纯干货)

程序员小猴紫&#xff0c;不错过任何一次干赚钱干货 1.兼职薪资&#xff0c;附行哥工资单2.兼职门槛&#xff0c;附学习知识清单3.兼职途径&#xff0c;附入职考核过程4.我的兼职感受 答应小猴紫的第一篇赚钱干货推文来啦&#xff0c;行哥第一个在读书期间通过兼职赚到的10w收…

Web前端大作业—里约热内卢奥运会(html+css+javascript)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

产品经理 - 产品设计方法论需求分析部分

整体 – 产品设计方法论思维导图 个人整理&#xff0c;存在异议大家可以讨论下 需求分析方法论 需求分析为需求收集的延展&#xff0c;需求收集后即需进行需求分析&#xff0c;拆解需求后方可业务落地&#xff0c;此处我将其分为两步&#xff0c;一是主动发散型需求分析&am…

移动端项目(第十九课)Vite+Vant组件环境配置

常用到的环境配置时不我待(第十八课)项目环境搭建_星辰镜的博客-CSDN博客 在上面的环境的基础上加上下面的一下配置 Normalize.css: Make browsers render all elements more consistently. (necolas.github.io) 介绍 | Pinia 中文文档 (web3doc.top) Day.js 中文文档 - 2kB 大…

【Java版oj】day02排列子序列

目录 一、原题再现 二、问题分析 三、完整代码 一、原题再现 链接&#xff1a;排序子序列_牛客笔试题_牛客网 来源&#xff1a;牛客网 [编程题]排序子序列 热度指数&#xff1a;10105 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 32M&…

水果店引流活动推荐_分享水果店微信小程序制作步骤

试试做个小程序拯救你的店&#xff01;让你做出有效果的活动&#xff0c;每笔钱都花在刀刃上&#xff01; 第一&#xff0c;提升水果销量&#xff0c;降低损耗 用水果店小程序做拼团、砍价、秒杀活动&#xff0c;并讲原本卖不完的水果&#xff0c;做成果盘吸引客人注册会员。上…

Manufactoria介绍及各关卡解法

Manufactoria解法Manufactoria基本介绍解法RobotoastRoboManufactoria基本介绍 Manufactoria是一个以制造工厂为背景的程序设计游戏。在游戏中&#xff0c;玩家需要在有限的平面空间中巧妙地排布传送带&#xff0c;打点器与分支器&#xff0c;完成识别或改写特定模式的字符串的…