软件设计之SSM(5)

news2024/11/25 20:51:19

软件设计之SSM(5)

路线图推荐:
【Java学习路线-极速版】【Java架构师技术图谱】
尚硅谷新版SSM框架全套视频教程,Spring6+SpringBoot3最新SSM企业级开发
资料可以去尚硅谷官网免费领取

学习内容:

AOP面向切面编程

  1. 代理
  2. AOP面向切面编程
  3. 获取通知细节信息
  4. 切点表达式
  5. 环绕通知
  6. 切面优先级

1、代理

代理模式是一种结构型设计模式,它的核心思想是:在访问对象时,通过代理对象进行间接访问。

静态代理

静态代理是指在编译时,代理类就已经存在,并且程序运行前,代理类的字节码已经生成。代理类和被代理类实现相同的接口,代理类内部调用被代理类的方法,同时在方法前后添加额外的逻辑。
静态代理的结构:

  • 接口:定义代理类和被代理类都要实现的方法。
  • 被代理类:实现接口,包含实际的业务逻辑。
  • 代理类:也实现接口,在调用被代理类方法的前后加入额外逻辑。
  • 缺点: 静态代理的一个主要缺点是,代理类与被代理类实现了相同的接口,当业务类(被代理类)变多时,你需要为每个类创建一个代理类,代码量大,且难以维护。

动态代理

动态代理相比静态代理更加灵活,它允许在运行时动态生成代理对象,避免了手动编写代理类的繁琐。Java 提供了两种动态代理机制:

  • JDK 动态代理: 适用于代理实现了接口的类(需要被代理的目标类必须实现接口,他会根据目标类的接口动态生成一个代理对象)。
    • 这个代理类对象:是一个动态生成的类的实例,这个动态生成的类实现你提供的接口。
  • CGLIB 动态代理: 适用于代理没有实现接口的类,通过字节码技术生成子类实现代理。
JDK动态代理介绍
  • Proxy.newProxyInstance() 方法是 JDK 动态代理的关键,用来生成代理对象。它在运行时创建一个代理它接受三个参数:
    • ClassLoader: 代理类的类加载器,一般使用目标对象的类加载器。
    • Interfaces: 代理对象实现的接口列表,这些接口也必须是目标对象实现的接口。
    • InvocationHandler: 代理对象的方法调用处理器。[实现具体要执行的非核心动作]
  • InvocationHandler是 JDK 动态代理的核心。它用于拦截代理对象的方法调用,并在此基础上执行代理逻辑。通过实现 InvocationHandler 接口的 invoke() 方法,代理可以在方法调用之前、之后或替代原始方法逻辑。
  • invoke() 方法接收三个参数(invoke() 方法会通过反射调用目标对象的实际方法):
    • proxy: 代理对象本身
    • method: 被调用的方法对象(目标类的方法)
    • args: 调用该方法时传递的参数。

2、AOP面向切面编程

AOP与OOP思维差异

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过分离横切关注点(Cross-Cutting Concerns)来增强代码的模块化。而OOP(Object-Oriented Programming,面向对象编程)则是一种通过类和对象来封装数据和行为的编程范式。

思维OOP(面向对象编程)AOP(面向切面编程)
核心理念通过类和对象封装数据和行为,实现代码复用、封装和多态等特性。通过分离横切关注点,将与业务逻辑无关的代码(如日志、事务等)从主业务逻辑中剥离。
关注点主要关注对象及其行为,强调如何设计对象以及对象之间的交互关系。主要关注横切关注点,即那些分散在不同模块中的公共需求,如日志、权限、事务管理等。
代码组织OOP关注将相关的数据和行为放到同一个类中,使对象内部的实现细节对外部不可见。AOP关注如何将某些行为(如日志记录)提取到独立的模块(切面),通过切入点与业务逻辑无缝结合。
模块化强调使用类来组织模块。类是最基本的模块化单位。强调通过**切面(Aspect)**将横切关注点与业务逻辑分离,以增强模块化。
扩展方式使用继承和实现接口来扩展功能。使用切面切入点动态注入功能,无需修改现有代码。
代码侵入性如果需要引入日志、权限管理等,可能需要在类的多个方法中显式编写这些代码。通过声明式的方式(如注解),将横切关注点注入业务逻辑,减少代码的侵入性。
适用场景适合封装具有特定职责的对象或类,强调业务逻辑的实现。适合处理分散的、重复的横切关注点,如日志、事务、异常处理等。

AOP核心名词理解

名词含义说明
横切关注点系统中跨多个模块的需求,如日志或事务(非核心代码处理场景)。
通知(增强)在特定连接点执行的增强代码,如前置通知或后置通知。
连接点程序执行中的特定点,如方法调用。
切入点匹配连接点的表达式,用于定义哪些方法/类需要增强。
切面横切关注点的具体实现,包含通知和切入点。
目标对象被增强(要被切入)的对象。
代理实际执行增强逻辑的对象,代替目标对象执行方法。
织入将切面与目标对象绑定的过程,通常在运行时完成。

快速实现

spring-aop: 用于引入Spring的AOP功能,支持基于代理的AOP(JDK动态代理和CGLIB代理)。
spring-aspects: 用于引入对AspectJ的支持,提供编译时、加载时和注解式的AspectJ功能。
@Aspect: 表示这个类是一个切面类
@Before: 是 Spring AOP 中的一个通知类型,表示在目标方法执行之前要执行的增强逻辑。
切入点表达式: 用于定义在哪些连接点上应用增强逻辑。

步骤关键点描述
1. 引入依赖pom.xml中加入spring-aopspring-aspects依赖,确保项目支持AOP。
2. 编写目标对象创建业务类或接口,定义需要被增强的业务逻辑。
3. 定义切面类使用@Aspect注解定义切面类,利用@Before@After等通知注解添加增强逻辑,使用@Pointcut定义切入点。
4. 启用AspectJ支持在配置类中使用@EnableAspectJAutoProxy注解,开启Spring AOP的注解支持。
5. 编写控制器或测试创建控制器或其他测试类,调用目标类的方法,验证切面的增强逻辑。
6. 测试并验证运行项目,通过访问控制器或测试类,检查AOP增强是否按预期工作。

3、获取通知细节信息

获取通知细节信息指的是在通知方法(如@Before、@After等)中获取有关被增强的目标方法的信息,例如方法名、参数、目标对象、类名等。

JointPoint接口

需要获取方法签名、传入的实参等信息时,可以在通知方法声明JoinPoint类型的形参

// @Before注解标记前置通知方法
// value属性:切入点表达式,告诉Spring当前通知方法要套用到哪个目标方法上
// 在前置通知方法形参位置声明一个JoinPoint类型的参数,Spring就会将这个对象传入
// 根据JoinPoint对象就可以获取目标方法名称、实际参数列表
@Before(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogBeforeCore(JoinPoint joinPoint) {
    
    // 1.通过JoinPoint对象获取目标方法签名对象
    // 方法的签名:一个方法的全部声明信息
    Signature signature = joinPoint.getSignature();
    
    // 2.通过方法的签名对象获取目标方法的详细信息
    String methodName = signature.getName();
    System.out.println("methodName = " + methodName);
    
    int modifiers = signature.getModifiers();
    System.out.println("modifiers = " + modifiers);
    
    String declaringTypeName = signature.getDeclaringTypeName();
    System.out.println("declaringTypeName = " + declaringTypeName);
    
    // 3.通过JoinPoint对象获取外界调用目标方法时传入的实参列表
    Object[] args = joinPoint.getArgs();
    
    // 4.由于数组直接打印看不到具体数据,所以转换为List集合
    List<Object> argList = Arrays.asList(args);
    
    System.out.println("[AOP前置通知] " + methodName + "方法开始了,参数列表:" + argList);
}

获取返回值

在返回通知中,通过@AfterReturning 注解的returning属性获取目标方法的返回值!

// @AfterReturning注解标记返回通知方法
// 在返回通知中获取目标方法返回值分两步:
// 第一步:在@AfterReturning注解中通过returning属性设置一个名称
// 第二步:使用returning属性设置的名称在通知方法中声明一个对应的形参
@AfterReturning(
        value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))",
        returning = "targetMethodReturnValue"
)
public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
    
    String methodName = joinPoint.getSignature().getName();
    
    System.out.println("[AOP返回通知] "+methodName+"方法成功结束了,返回值是:" + targetMethodReturnValue);
}

异常对象捕捉

在异常通知中,通过@AfterThrowing注解的throwing属性获取目标方法抛出的异常对象

// @AfterThrowing注解标记异常通知方法
// 在异常通知中获取目标方法抛出的异常分两步:
// 第一步:在@AfterThrowing注解中声明一个throwing属性设定形参名称
// 第二步:使用throwing属性指定的名称在通知方法声明形参,Spring会将目标方法抛出的异常对象从这里传给我们
@AfterThrowing(
        value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))",
        throwing = "targetMethodException"
)
public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
    
    String methodName = joinPoint.getSignature().getName();
    
    System.out.println("[AOP异常通知] "+methodName+"方法抛异常了,异常类型是:" + targetMethodException.getClass().getName());
}

4、切点表达式

在这里插入图片描述

  1. 权限修饰符:public private 直接描述对应修饰符即可
  2. 方法返回值 int String void 直接描述返回值类型,其中execution(*) 表示 不考虑权限修饰符与方法返回值
  3. 指定包的地址
 固定的包: com.atguigu.api | service | dao
 单层的任意命名: com.atguigu.*  = com.atguigu.api  com.atguigu.dao  * = 任意一层的任意命名
 任意层任意命名: com.. = com.atguigu.api.erdaye com.a.a.a.a.a.a.a  ..任意层,任意命名 用在包上!
 注意: ..不能用作包开头   public int .. 错误语法  com..
 找到任何包下: *..
  1. 指定类名称
固定名称: UserService
任意类名: *
部分任意: com..service.impl.*Impl
任意包任意类: *..*
  1. 指定方法名称:语法和类名一致
  2. 方法参数
       具体值: (String,int) != (int,String) 没有参数 ()
       模糊值: 任意参数 有 或者 没有 (..)  ..任意参数的意识
       部分具体和模糊:
         第一个参数是字符串的方法 (String..)
         最后一个参数是字符串 (..String)
         字符串开头,int结尾 (String..int)
         包含int类型(..int..)

切点表达式的复用

增强方法的切点表达式容易冗余,也不方便统一维护。我们可以将切点提取,在增强上进行引用即可!
切点统一管理:将需要被引用的切点表达式方法统一放在类中管理

  • 在需要使用表达式的地方调用即可
@Before(value = "com.atguigu.AtguiguPointCut.atguiguGlobalPointCut()")
@Component
public class AtguiguPointCut {
    
    @Pointcut(value = "execution(public int *..Calculator.sub(int,int))")
    public void atguiguGlobalPointCut(){}
    
    @Pointcut(value = "execution(public int *..Calculator.add(int,int))")
    public void atguiguSecondPointCut(){}
    
    @Pointcut(value = "execution(* *..*Service.*(..))")
    public void transactionPointCut(){}
}

5、环绕通知

环绕通知(@Around)是 Spring AOP 中的一种通知类型,允许你在目标方法执行的前后插入自定义的逻辑。它是最强大和灵活的通知类型,因为你可以控制目标方法的执行,也可以决定是否执行该方法,并可以修改方法的参数和返回值。

  • userServiceMethods() 方法定义了切入点,匹配 UserService 中的所有方法。
  • logAround(ProceedingJoinPoint joinPoint) 方法是环绕通知。
  • 调用 joinPoint.proceed() 方法执行目标方法
@Aspect
@Component
public class LoggingAspect {

    // 定义切入点,匹配 UserService 中的所有方法
    @Pointcut("execution(* com.example.service.UserService.*(..))")
    public void userServiceMethods() {}

    // 环绕通知
    @Around("userServiceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) {
        Object result = null; // 用于存储目标方法的返回值
        try {
            // 获取方法名
            String methodName = joinPoint.getSignature().getName();
            // 获取方法参数
            Object[] methodArgs = joinPoint.getArgs();
            
            // 在方法执行前执行的逻辑
            System.out.println("Logging: Before method: " + methodName);
            System.out.println("Arguments: " + Arrays.toString(methodArgs));

            // 调用目标方法
            result = joinPoint.proceed(); // 执行目标方法

            // 在方法执行后执行的逻辑
            System.out.println("Logging: After method: " + methodName);
            System.out.println("Result: " + result);
        } catch (Throwable throwable) {
            // 处理目标方法抛出的异常
            System.err.println("Logging: Exception in method: " + joinPoint.getSignature().getName());
            System.err.println("Exception message: " + throwable.getMessage());
            // 重新抛出异常,便于后续处理
            throw throwable;
        }
        return result; // 返回目标方法的返回值
    }
}

6、切面优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用 @Order 注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低
    在这里插入图片描述

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

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

相关文章

好用的股票预测八大算法的Python实现

股票预测算法通常涉及时间序列分析、统计学、机器学习和深度学习等多种方法。以下是经典的、常见的十大股票预测算法及其Python实现。这些算法各有优势&#xff0c;可以用于不同的市场预测场景。以下代码实现中&#xff0c;我们将使用yfinance下载数据&#xff0c;并展示各算法…

如何创建商业博客:一步一步教你从零开始

搭建一个成功的商业博客&#xff0c;不仅可以提升品牌形象&#xff0c;还能吸引更多潜在客户。作为一个在这方面有些经验的人&#xff0c;我来分享一些实用的步骤和建议&#xff0c;希望对你有所帮助。 一、明确你的目标和客户群体 确定目标&#xff1a;首先&#xff0c;你得搞…

Origin图像中插入各种符号以及矩形椭圆等

画各种形状 空白处右击 文字处右击

【RISCV指令集手册】向量扩展v1.0

概述 从rvv 0.9说起 此前写过向量扩展0.9的阅读记录&#xff0c;三年已过&#xff0c;本以为不再参与RVV的相关开发&#xff0c;奈何造化弄人&#xff0c;旧业重操&#xff0c;真就世事难料呀。 总的来说1.0版本相比0.9版本的扩充了较多内容&#xff0c;但大部分为指令功能的…

力扣1~5题

题1&#xff08;简单&#xff09;. 思路&#xff1a; 因为时间复杂度小于n^2,所以不能双for遍历&#xff0c;怎么优化&#xff1f; 这里采用一个键值对的形式&#xff0c;存储nums离target的间隔和它的下标&#xff0c;只要n&#xff0c;然后再遍历nums有没有刚好是这个距离的就…

cnn突破四(生成卷积核与固定核对比)

cnn突破三中生成四个卷积核&#xff0c;训练6万次&#xff0c;91分&#xff0c;再训练6万次&#xff0c;95分&#xff0c;不是很满意&#xff0c;但又找不到问题点&#xff0c;所以就想了个办法&#xff0c;使用三个固定核&#xff0c;加上三层bpnet神经网络&#xff0c;看看效…

从零开始学cv-16:超像素图像分割

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、超像素图像分割简介二&#xff1a;SLIC超像素图像分割三&#xff1a;Seed超像素分割算法四 &#xff1a;LSC超像素分割 前言 在数字图像处理和计算机视觉领…

2、Redis数据安全性分析

文章目录 一、Redis性能压测脚本介绍二、Redis数据持久化机制详解1、整体介绍Redis的数据持久化机制2、RDB详解3、AOF详解4、混合持久化策略 三、Redis主从复制Replica机制详解**1、Replica是什么&#xff1f;有什么用&#xff1f;****2、如何配置Replica&#xff1f;****3、如…

【AI知识点】正态分布(高斯分布)和中心极限定理(CLT)

正态分布&#xff08;Normal Distribution&#xff09;和中心极限定理&#xff08;Central Limit Theorem, CLT&#xff09; 是统计学中非常重要的概念&#xff0c;它们广泛应用于概率论、数据分析、机器学习等领域。以下将详细解释这两个概念及其关系。 1. 正态分布&#xff…

【C++】入门基础介绍(上)C++的发展历史与命名空间

文章目录 1. 前言2. C发展历史2. 1 C版本更新特性一览2. 2 关于C23的一个小故事: 3. C的重要性3. 1 编程语言排行榜3. 2 C在工作领域中的应用 4. C学习建议和书籍推荐4. 1 C学习难度4. 2 学习书籍推荐 5. C的第一个程序6. 命名空间6. 1 namespace的价值6. 2 namespace的定义6. …

《CUDA编程》4.CUDA程序的错误检测

在编写CUDA程序时&#xff0c;有的错误在编译过程中被发现&#xff0c;称为编译错误&#xff0c;有的在运行时出现&#xff0c;称为运行时刻错误&#xff0c;本章讨论如何排查运行时刻错误 1 一个检测CUDA运行时错误的宏函数 1.1 编写错误检查宏函数 在《CUDA编程》3.简单CUD…

从0到1:培训机构排课小程序开发笔记一

业务调研 随着人们生活水平的提高&#xff0c;健康意识和学习需求日益增强&#xff0c;私教、健身和培训机构的市场需求迅速增长。高效的排课系统不仅可以提升机构的管理效率&#xff0c;还能提高学员的满意度。解决传统的排课方式存在的时间冲突、信息不对称、人工操作繁琐等…

51单片机的家用煤气报警系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温度传感器CO传感器蓝牙LED、蜂鸣器等模块构成。适用于家用天然气泄露报警器、煤气泄露报警器、无线报警等相似项目。 可实现功能: 1、LCD1602实时显示温度和煤气浓度 2、温度传感器DS18B20采集环境温度 3、CO传…

【Mybatis篇】Mybatis的关联映射详细代码带练 (多对多查询、Mybatis缓存机制)

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】,【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;一.关联映射概述 &#x1f6a…

【BUG】P-tuningv2微调ChatGLM2-6B时所踩的坑

0.前言 P-tuning v2的实验在网上一抓一大把&#xff0c;这里就说一下我在微调过程中遇到的有些bug&#xff0c;踩过的一些坑&#xff0c;在网上找了很久都没有一些好的解决方案&#xff0c;在这里记录一下。 1.下载预训练模型 在官方给出的教程中&#xff0c;并不需要预先将模…

【springboot】简易模块化开发项目整合Swagger2

接上一项目【springboot】简易模块化开发项目整合MyBatis-plus&#xff0c;进行拓展项目 1.新建模块 右键项目→New→Module&#xff0c;新建一个模块 父项目选择fast-demo&#xff0c;命名为fast-demo-config&#xff0c;用于存放所有配置项 添加后&#xff0c;项目结构如图…

X3U·可编程控制器的定位控制

FX3U可编程控制器的定位控制进行说明。 一、概要 FX3U可编程控制器可以向伺服电机、步进电机等输出脉冲信号&#xff0c;从而进行定位控制。 脉冲频率高的时候&#xff0c;电机转得快:脉冲数多的时候&#xff0c;电机转得多。用脉冲频率、脉冲数来设定定位对象…

Linux基本命令及vim应用实训练习

Linux基本命令及vim应用实训练习 1. 2. 3. 4. 5. 使用man cp找出

4 思科模拟器的介绍和使用

4 思科模拟器的介绍和使用 思科的IOS给我们提供了三大模式 设备开机后&#xff0c;进入的模式是【用户模式】 Router表示设备的名称 “>”表示用户模式 在用户模式输入"?" 可列出在用户模式可以使用的命令 第二种模式是特权模式,输入enable进入特权模式&…

RNN经典案例——构建人名分类器

RNN经典案例——人名分类器 一、数据处理1.1 去掉语言中的重音标记1.2 读取数据1.3 构建人名类别与人名对应关系字典1.4 将人名转换为对应的onehot张量 二、构建RNN模型2.1 构建传统RNN模型2.2 构建LSTM模型2.3 构建GRU模型 三、构建训练函数并进行训练3.1 从输出结果中获得指定…