Spring 核心技术解析【纯干货版】- IV:Spring 切面编程模块 Spring-Aop 模块精讲

news2025/1/6 4:20:10

随着软件开发技术的不断进步,面向切面编程(AOP)作为一种重要的编程思想,已经在现代开发中占据了重要地位。它通过将横切逻辑从业务逻辑中分离出来,使得代码更加清晰、易于维护。Spring AOP 作为 Spring 框架的核心模块之一,为开发者提供了简单且强大的 AOP 支持,使我们能够以更加优雅的方式处理日志记录、安全控制、事务管理等常见的横切逻辑。本篇内容将引导您深入理解 Spring AOP 的基本概念、核心原理以及实战操作,帮助您在实际项目中更加得心应手地运用这一强大工具。


文章目录

      • 1、Spring-Aop 模块介绍
        • 1.1、Spring-Aop 模块概述
        • 1.2、Spring-Aop 模块依赖
        • 1.3、Spring-Aop 模块作用
      • 2、Spring-Aop 模块补充
        • 2.1、AOP 前置概念
        • 2.2、Spring AOP的实现
        • 2.3、通知类型
          • 2.3.1、前置通知(Before Advice)
          • 2.3.2、后置通知(After Returning Advice)
          • 2.3.3、后置异常通知(After Throwing Advice)
          • 2.3.4、最终通知(After Advice)
          • 2.3.5、环绕通知(Around Advice)
      • 3、基于注解的 Spring AOP 开发
        • 3.1、依赖引入
        • 3.2、Spring 业务类
        • 3.3、Spring 切面类
        • 3.4、Spring 配置类
        • 3.5、Spring 主函数
      • X、后记


1、Spring-Aop 模块介绍

1.1、Spring-Aop 模块概述

Spring AOP 模块,是 Spring 提供的一个面向切面编程(Aspect Oriented Programming,AOP)的模块。

Spring AOP 通过灵活的配置和强大的功能,可以轻松地将横切关注点从业务逻辑中分离出来,提高代码的可维护性和可扩展性。

1.2、Spring-Aop 模块依赖

Spring-AOP 模块的依赖有两个,分别是 Spring-Beans 模块和 Spring-Core 模块。

其中 Spring Beans 模块是对 Spring Bean 进行定义,实现 IOC 基础功能的模块。而 Spring-Core 是 Spring 中的基础模块,它提供了框架运行所必需的核心功能。

1.3、Spring-Aop 模块作用

Spring AOP 模块本身不具备完整的 AOP 能力,需要依赖其他模块和工具(例如 AspectJ 或代理机制)来实现 AOP 功能。这是因为 Spring AOP 本质上是一个基于代理的 AOP 实现框架,它利用 Spring 容器和其他技术来实现横切逻辑。Spring AOP 模块本身不具备完整的 AOP 能力,需要依赖其他模块和工具(例如 AspectJ 或代理机制)来实现 AOP 功能。这是因为 Spring AOP 本质上是一个基于代理的 AOP 实现框架,它利用 Spring 容器和其他技术来实现横切逻辑。


2、Spring-Aop 模块补充

2.1、AOP 前置概念

首先,我们来确认一些 Spring 使用核心的 AOP 概念和术语。Spring 官网的原话是:这些术语并非 Spring 独有,遗憾的是,AOP 术语并不是特别直观。但是,如果 Spring 使用自己的术语,那就更加令人困惑了。

核心术语(逻辑层面,些概念描述的是 AOP 的基本组成部分):

  • 横切(Cross-cutting Concern):横切是指在程序的多个模块中反复出现的功能或逻辑,通常与业务逻辑无直接关系,但又不可或缺。这些功能通常是 “横向” 作用于程序的各个模块,而不是某个模块的核心业务逻辑的一部分,因此被称为横切关注点。
  • 切面(Aspect):切面是 AOP 处理横切关注点的核心单元,它是对 横切逻辑的模块化封装,它包含切点和通知(切面 = 切点 + 通知)。切面的本质是一个包含横切逻辑的类,定义了横切逻辑执行的时机(通知)和适用的位置(切点)。其目的是让横切逻辑独立于业务代码,提高代码模块化程度。换句话说横切是问题,切面是解决方案。
  • 连接点(Join Point):连接点就是程序中可以插入切面逻辑的具体点。例如,一个类中的有多个方法,那么每个方法都是潜在的连接点。
  • 通知(Advice): 通知是在特定连接点处执行的动作。有多种类型的通知,如前置通知、后置通知、环绕通知等。
  • 切点(Pointcut):切点从所有切入点中筛选出需要增强的具体点的过滤条件。通过定义一套过滤条件,决定哪些连接点将会被增强(即在这些连接点执行通知)。这些连接点是程序中可以插入横切逻辑的位置(例如方法调用)。

核心术语(实现层面,这些概念描述的是 AOP 的技术实现方式和执行过程):

  • 引入(Introduction): 动态地为目标类添加新的接口或功能,是一种特殊的增强方式。例如,为一个类增加性能监控的能力,不改变原始代码。
  • 织入(Weaving):将切面逻辑(通知、引入)和目标类的代码结合的过程,生成增强后的类对象。织入可以在编译时、类加载时或运行时进行。
2.2、Spring AOP的实现

Spring AOP 基于代理实现,主要有两种方式:

  1. JDK 动态代理:适用于接口代理。
  2. CGLIB 代理:适用于类代理。

JDK 动态代理:基于 Java 的内置动态代理机制。代理目标对象的接口,而不是类本身。当目标对象实现了一个或多个接口时,Spring 会默认选择 JDK 动态代理。

  • 实现方式:创建一个实现了目标接口的代理类,并在方法调用前后插入增强逻辑。使用 java.lang.reflect.ProxyInvocationHandler
  • 优点:无需引入额外的库(纯 JDK 实现)。代理生成速度较快。
  • 局限性:目标类必须有接口。

CGLIB 代理:基于字节码生成技术,由 CGLIB(Code Generation Library)库支持。通过继承目标类生成代理类,覆盖目标类中的方法来实现增强。当目标类没有实现任何接口时,Spring 会使用 CGLIB 代理。

  • 实现方式:通过字节码操作生成一个目标类的子类,并对其方法进行拦截。
  • 优点:可以代理没有实现接口的类。代理范围更广(包括普通类)。
  • 局限性:目标类或方法不能声明为 final,否则无法被继承和代理。需要引入额外的 CGLIB 库。代理生成速度较慢,但运行时性能较好。
2.3、通知类型

Spring AOP 提供了以下几种通知类型,每种通知用于在不同的时间点插入增强逻辑。

通知类型执行时机注解
前置通知在目标方法执行前@Before
后置通知在目标方法成功返回后@AfterReturning
后置异常通知在目标方法抛出异常后@AfterThrowing
最终通知在目标方法执行后(无论成功与否)@After
环绕通知在目标方法前后都执行,可控制目标方法的执行@Around
2.3.1、前置通知(Before Advice)

在目标方法执行之前执行。

@Aspect
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}
2.3.2、后置通知(After Returning Advice)

在目标方法成功执行之后执行。

@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
    System.out.println("After returning: " + joinPoint.getSignature().getName());
    System.out.println("Result: " + result);
}
2.3.3、后置异常通知(After Throwing Advice)

在目标方法抛出异常后执行。

@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
    System.out.println("Exception in method: " + joinPoint.getSignature().getName());
    System.out.println("Exception: " + error);
}
2.3.4、最终通知(After Advice)

在目标方法执行之后执行,无论方法是否成功执行。

@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
    System.out.println("After method: " + joinPoint.getSignature().getName());
}
2.3.5、环绕通知(Around Advice)

在目标方法执行前后都执行,可以完全控制目标方法的执行。

@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("Before method: " + joinPoint.getSignature().getName());
    Object result = joinPoint.proceed(); // 执行目标方法
    System.out.println("After method: " + joinPoint.getSignature().getName());
    return result;
}

3、基于注解的 Spring AOP 开发

3.1、依赖引入

虽然 Spring AOP 是一个模块,但它需要依赖以下组件来完整实现 AOP 功能:

  • Spring 核心模块:必须依赖 Spring 的核心模块(如 spring-contextspring-beans),用于 Bean 的创建和管理。如果没有容器,Spring AOP 只能手动配置,使用起来非常受限。
  • 代理机制:依赖 JDK 动态代理或 CGLIB 来生成代理对象。
  • AspectJ(可选):Spring AOP 支持 AspectJ 注解,但必须引入 aspectjweaver 库。不依赖 AspectJ 的编译时或加载时织入器。
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.39</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.39</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.22.1</version>
        </dependency>
    </dependencies>
3.2、Spring 业务类
package com.lizhengi.example;

import org.springframework.stereotype.Component;

/**
 * 用户业务类
 */
@Component // 标记为 Spring 管理的 Bean
public class UserService {

    public void createUser(String name) {
        System.out.println("User " + name + " created.");
    }

    public void deleteUser(String name) {
        System.out.println("User " + name + " deleted.");
    }
}
3.3、Spring 切面类
package com.lizhengi.example;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import org.springframework.stereotype.Component;

@Aspect // 声明这是一个切面
@Component // 标记为 Spring 管理的 Bean
public class LoggingAspect {

    @Before("execution(* com.lizhengi.example.UserService.*(..))") // 定义切点
    public void logBefore() {
        System.out.println("Logging before method execution");
    }
}

说明:

  1. @Aspect:标记为切面。
  2. @Before:定义一个前置通知,在方法执行前触发。
  3. 切点表达式:execution(* com.example.service.UserService.*(..)) 表示匹配 UserService 中的所有方法。
3.4、Spring 配置类
package com.lizhengi.example;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration // 声明为配置类
@ComponentScan(basePackages = "com.lizhengi") // 自动扫描指定包
@EnableAspectJAutoProxy // 启用 AOP
public class AppConfig {}

说明:

  1. @Configuration:标记这是一个 Spring 配置类。
  2. @ComponentScan:指定要扫描的包,加载所有带有 @Component 的类。
  3. @EnableAspectJAutoProxy:启用 AOP 的自动代理。
3.5、Spring 主函数
package com.lizhengi;

import com.lizhengi.example.AppConfig;
import com.lizhengi.example.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        // 加载 Spring 应用上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        // 获取 UserService 的代理对象
        UserService userService = context.getBean(UserService.class);
        // 调用方法,触发 AOP 增强
        userService.createUser("Alice");
        userService.deleteUser("Bob");
        // 关闭上下文
        context.close();
    }
}

运行结果:

Logging before method execution
User Alice created.
Logging before method execution
User Bob deleted.

X、后记

在学习和实践 Spring AOP 的过程中,我们不仅能深入了解其实现机制,还能感受到面向切面编程对开发效率和代码质量的提升作用。从初识 AOP 的基础概念到动手实现切面逻辑,每一步都在为我们构建更加清晰、健壮的应用体系奠定基础。希望本篇内容能为您打开通往 AOP 世界的大门,也期待您在实际工作中灵活运用所学,将代码的复杂度隐藏于优雅的设计之中,为团队和项目带来更大的价值。如果您有任何疑问或见解,欢迎随时交流与探讨!

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

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

相关文章

接受Header使用错Map类型,导致获取到的Header值不全

问题复现 在 Spring 中解析 Header 时&#xff0c;我们在多数场合中是直接按需解析的。例如&#xff0c;我们想使用一个名为 myHeaderName 的 Header&#xff0c;我们会书写代码如下&#xff1a;RequestMapping(path "/hi", method RequestMethod.GET) public Str…

GitHub的简单操作

引言 今天开始就要开始做项目了&#xff0c;上午是要把git搭好。搭的过程中遇到好多好多的问题。下面就说一下git的简单操作流程。我们是使用的GitHub,下面也就以这个为例了 一、GitHub账号的登录注册 https://github.com/ 通过这个网址可以来到GitHub首页 点击中间绿色的S…

【时时三省】(C语言基础)常见的动态内存错误

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 对NULL指针的解引用操作 示例&#xff1a; malloc申请空间的时候它可能会失败 比如我申请一块非常大的空间 那么空间可能就会开辟失败 正常的话要写一个if&#xff08;p&#xff1d;&#x…

【51项目】51单片机自制小霸王游戏机

视频演示效果&#xff1a; 纳新作品——小霸王游戏机 目录&#xff1a; 目录 视频演示效果&#xff1a; 目录&#xff1a; 前言&#xff1a; 一、连接方式&#xff1a; 1.1 控制引脚 1.2. 显示模块 1.3. 定时器 1.4. 游戏逻辑与硬件结合 1.5. 中断处理 二、源码分析&#xff1a…

ESP32-S3遇见OpenAI:OpenAI官方发布ESP32嵌入式实时RTC SDK

目录 OpenAI RTC SDK简介应用场景详解智能家居控制系统个人健康助手教育玩具 技术亮点解析低功耗设计快速响应高精度RTC安全性保障开发者指南 最近&#xff0c;OpenAI官方发布了一款针对ESP32-S3的嵌入式实时RTC&#xff08;实时时钟&#xff09;SDK&#xff0c;这标志着ESP32-…

Elasticsearch:减少 Elastic 容器镜像中的 CVE(常见的漏洞和暴露)

作者&#xff1a;来自 Elastic Maxime Greau 在这篇博文中&#xff0c;我们将讨论如何通过在 Elastic 产品中切换到最小基础镜像并优化可扩展漏洞管理程序的工作流程来显著减少 Elastic 容器镜像中的常见漏洞和暴露 (Common Vulnerabilities and Exposures - CVEs)。 基于 Chai…

计算机网络 (21)网络层的几个重要概念

前言 计算机网络中的网络层是OSI&#xff08;开放系统互连&#xff09;模型中的第三层&#xff0c;也是TCP/IP模型中的第二层&#xff0c;它位于数据链路层和传输层之间&#xff0c;负责数据包从源主机到目的主机的路径选择和数据转发。 一、网络层的主要功能 路由选择&#xf…

LED背光驱动芯片RT9293应用电路

一&#xff09;简介&#xff1a; RT9293 是一款高频、异步的 Boost 升压型 LED 定电流驱动控制器&#xff0c;其工作原理如下&#xff1a; 1&#xff09;基本电路结构及原理 RT9293的主要功能为上图的Q1. Boost 电路核心原理&#xff1a;基于电感和电容的特性实现升压功能。当…

第四届计算机、人工智能与控制工程

第四届计算机、人工智能与控制工程 The 4th International Conference on Computer, Artificial Intelligence and Control Engineering 重要信息 大会官网&#xff1a;www.ic-caice.net 大会时间&#xff1a;2025年1月10-12日 大会地点&#xff1a;中国合肥 (安徽大学磬苑…

【Rust 学习笔记】Rust 基础数据类型介绍——指针、元组和布尔类型

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 博客内容主要围绕&#xff1a; 5G/6G协议讲解 高级C语言讲解 Rust语言讲解 文章目录 Rust 基础数据类型介绍——指针、元组和布尔类型一、元组类型…

YOLO系列的学习

YOLOV1全解 You Only Look Once&#xff0c;把检测问题转化成回归问题&#xff0c;一个CNN就搞定了&#xff01;&#xff01;&#xff01;效率高&#xff0c;可对视频进行实时检测&#xff0c;应用领域非常广&#xff0c;到V3的时被美国军方用于军事行动&#xff0c;作者出于某…

鸿蒙应用开发搬砖经验之—使用DevTools工具调试前端页面

环境说明&#xff1a; 系统环境&#xff1a;Mac mini M2 14.5 (23F79) 开发IDE&#xff1a;DevEco Studio 5.0.1 Release 配置步骤&#xff1a; 按着官方的指引来慢慢一步一步来&#xff0c;但前提是要配置好SDK的路径&#xff08;没有配置的话&#xff0c;可能先看下面的配…

计算机网络练习题

学习这么多啦&#xff0c;那就简单写几个选择题巩固一下吧&#xff01; 1. 在IPv4分组各字段中&#xff0c;以下最适合携带隐藏信息的是&#xff08;D&#xff09; A、源IP地址 B、版本 C、TTL D、标识 2. OSI 参考模型中&#xff0c;数据链路层的主要功能是&#xff08;…

Django REST framework 源码剖析-视图类详解(Views)

Django REST framework视图图解 视图类&#xff08;View&#xff09; ‌视图‌是DRF中处理用户请求的基本单元。它们可以是函数视图&#xff08;FBV&#xff09;或类视图&#xff08;CBV&#xff09;。函数视图使用函数来处理请求&#xff0c;而类视图则使用类来处理请求。类视…

spring中使用@Validated,什么是JSR 303数据校验,spring boot中怎么使用数据校验

文章目录 一、JSR 303后台数据校验1.1 什么是 JSR303&#xff1f;1.2 为什么使用 JSR 303&#xff1f; 二、Spring Boot 中使用数据校验2.1 基本注解校验2.1.1 使用步骤2.1.2 举例Valid注解全局统一异常处理 2.2 分组校验2.2.1 使用步骤2.2.2 举例Validated注解Validated和Vali…

网页单机版五子棋小游戏项目练习-初学前端可用于练习~

今天给大家分享一个 前端练习的项目&#xff0c;技术使用的是 html css 和javascrpit 。希望能对于 刚刚学习前端的小伙伴一些帮助。 先看一下 实现的效果图 1. HTML&#xff08;HyperText Markup Language&#xff09; HTML 是构建网页的基础语言&#xff0c;它的主要作用是定…

UE5材质节点Distance

Distance可以计算两个物体间的距离&#xff0c;可以用来做过渡效果 当相机和物体距离3000的时候&#xff0c;就会渐渐从蓝过渡到红色&#xff0c;除以500是为了平滑过渡

【AIGC-ChatGPT进阶提示词指令】AI美食助手的设计与实现:Lisp风格系统提示词分析

引言 在人工智能助手的应用领域中&#xff0c;美食烹饪是一个既专业又贴近生活的方向。本文将详细分析一个基于Lisp风格编写的美食助手系统提示词&#xff0c;探讨其结构设计、功能实现以及实际应用效果。 提出你的菜系&#xff0c;为你分析&#xff0c;并生成图片卡片 提示词…

基于开发/发布/缺陷分离模型的 Git 分支管理实践20250103

基于开发/发布/缺陷分离模型的 Git 分支管理实践 引言 在现代软件开发中&#xff0c;合理的分支管理策略是保证项目成功的关键因素之一。本文将详细介绍一种基于开发/发布/缺陷分离的 Git 分支管理模型&#xff0c;这种模型不仅能提升团队协作效率&#xff0c;还能确保代码质…

【Cocos TypeScript 零基础 3.1】

目录 场景跳转 场景跳转 把新建好的TS文件与场景绑定 选中 场景 或 camera 拖进右边的 属性检查器 双击T文件,进入编辑 至于用什么IDE看个位朋友高兴 我这里有 VScode ,先用这个,老师也没有推荐 (老师也用的是这个) VScode UI 也有中文包,请自行上网搜索 打开创建的TS文件后…