周振林 周振林
首页
  • 前端文章

    • HTML
    • CSS
    • Tailwind CSS (opens new window)
    • JavaScript
    • Vue3
    • 其他
  • 学习笔记

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript》
    • 《Vue》
    • 《Git》
    • 《小程序笔记》
    • 《JS设计模式总结笔记》
  • 规范
  • Spring
  • 安装教程
  • 其他教程
  • 归真医学
  • 常用药材
  • 学习笔记
  • 经方学习心得
  • 基础
  • 虚拟化
  • Docker
  • OpenStack
  • 心情杂货
关于
收藏
  • 分类
  • 标签
  • 归档

周振林

IT界的小学生
首页
  • 前端文章

    • HTML
    • CSS
    • Tailwind CSS (opens new window)
    • JavaScript
    • Vue3
    • 其他
  • 学习笔记

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript》
    • 《Vue》
    • 《Git》
    • 《小程序笔记》
    • 《JS设计模式总结笔记》
  • 规范
  • Spring
  • 安装教程
  • 其他教程
  • 归真医学
  • 常用药材
  • 学习笔记
  • 经方学习心得
  • 基础
  • 虚拟化
  • Docker
  • OpenStack
  • 心情杂货
关于
收藏
  • 分类
  • 标签
  • 归档
  • 规范

  • Spring

    • Spring基础
    • Spring IoC
    • Spring AOP
      • Spring AOP 面向切面编程
        • 什么是 AOP
        • Aspect(切面)
        • 连接点(join point)
        • 切点(point cut)
        • Advice(增强)
        • 彻底理解 Aspect, join point, point cut, Advice
        • 案例
    • SpringBoot异常
    • SpringBoot过滤器
    • SpringBoot拦截器
    • Response设置响应编码
    • 依赖start和依赖BOM区别
    • Thymeleaf教程
  • 安装教程

  • 其他教程

  • 后端
  • Spring
周振林
2024-05-22
目录

Spring AOP

# Spring AOP 面向切面编程

# 什么是 AOP

AOP(Aspect-Oriented Programming,即 面向切面编程)与 OOP( Object-Oriented Programming,面向对象编程) 相辅相成,提供了与 OOP 不同的抽象软件结构的视角。

在 OOP 中,我们以类(class)作为我们的基本单元,而 AOP 中的基本单元是 Aspect(切面)

# Aspect(切面)

Aspect 由 pointcut 和 Advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中. AOP 的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:

如何通过 pointcut 和 Advice 定位到特定的 joinpoint 上 如何在 Advice 中编写切面代码. 可以简单地认为, 使用 @Aspect 注解的类就是切面.

# 连接点(join point)

a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理. 在 Spring AOP 中, join point 总是方法的执行点, 即只有方法连接点.

# 切点(point cut)

匹配 join point 的谓词(a predicate that matches join points). Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行. 在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice.

# Advice(增强)

由 Aspect 添加到特定的 join point(即满足 point cut 规则的 join point) 的一段代码. 许多 AOP 框架, 包括 Spring AOP, 会将 Advice 模拟为一个拦截器(interceptor), 并且在 join point 上维护多个 Advice, 进行层层拦截.

Advice 的类型

  • Before Advice, 在 join point 前被执行的 Advice. 虽然 before Advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before Advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
  • After return Advice, 在一个 join point 正常返回后执行的 Advice
  • After throwing Advice, 当一个 join point 抛出异常后执行的 Advice
  • After(final) Advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 Advice.
  • Around Advice, 在 join point 前和 joint point 退出后都执行的 Advice. 这个是最常用的 Advice.

# 彻底理解 Aspect, join point, point cut, Advice

看完了上面的理论部分知识, 我相信还是会有不少朋友感觉到 AOP 的概念还是很模糊, 对 AOP 中的各种概念理解的还不是很透彻. 其实这很正常, 因为 AOP 中的概念是在是太多了, 下面我以一个简单的例子来比喻一下 AOP 中 Aspect, jointpoint, pointcut 与 Advice 之间的关系.

让我们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来.

来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系. 首先我们知道, 在 Spring AOP 中 join point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, join point 就相当于 爪哇的小县城里的百姓, point cut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问. 为什么可以这样类比呢?

  • join point --> 爪哇的小县城里的百姓: 因为根据定义, join point 是所有可能被织入 Advice 的候选的点, 在 Spring AOP 中, 则可以认为所有方法执行点都是 join point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人.
  • point cut --> 男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 Advice, 但是我们并不希望在所有方法上都织入 Advice, 而 pointcut 的作用就是提供一组规则来匹配 joinpoint, 给满足规则的 joinpoint 添加 Advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问.
  • Advice --> 抓过来审问, Advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 join point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓.
  • Aspect: Aspect 是 point cut 与 Advice 的组合, 因此在这里我们就可以类比: "根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问" 这一整个动作可以被认为是一个 Aspect.

或则我们也可以从语法的角度来简单类比一下. 我们在学英语时, 经常会接触什么 定语, 被动句 之类的概念, 那么可以做一个不严谨的类比, 即 joinpoint 可以认为是一个 宾语, 而 pointcut 则可以类比为修饰 joinpoint 的定语, 那么整个 Aspect 就可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 Advice 操作.

# 案例

创建切面

LogAdvice


@Component
@Aspect
public class LogAdvice {

    /**
     *
     * jonit point :所有可以执行的方法
     *
     * point cut : 是符合定义了一些规则的方法
     *
     */

    @Pointcut("execution(* com.joe.service..*.*(..))")
    public void pointcut(){

    }

    @Before("pointcut()")
    public void beforeAdvice(){
        System.out.println("前置增强。。。。");
    }

    @AfterReturning("pointcut()")
    public void  afterReturn(){
        System.out.println("后置增强--正常返回");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("后置增强--异常返回");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("后置增强--最终返回");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("连接点之前----方法执行之前");

        Object obj=joinPoint.proceed();
        //OrderService.print()
        System.out.println("连接点之后----方法执行之后");

        return obj;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

配置切面 AppConfiguration

@Configuration
@EnableAspectJAutoProxy
public class AppConfiguration {
}
1
2
3
4

测试切面

OrderService

@Service
public class OrderService {

    public void print(){
        System.out.println("打印订单。。。。。");
    }

    public void print1(){
        System.out.println("打印订单1。。。。。");
    }

    public void saveLog(){
        System.out.println("保存订单日志。。。");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

App

public class App 
{
    public static void main( String[] args )
    {
        ApplicationContext context=new AnnotationConfigApplicationContext("com.joe");
        OrderService orderService=context.getBean(OrderService.class);
        orderService.print();
    }
}
1
2
3
4
5
6
7
8
9

源码地址 (opens new window)

Last Updated: 2024/07/26, 16:12:04
Spring IoC
SpringBoot异常

← Spring IoC SpringBoot异常→

最近更新
01
Docker安装
06-10
02
Docker运行JAR
06-10
03
Docker部署MySQL
06-10
更多文章>
Copyright © 2019-2025 鲁ICP备19032096号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式