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

快速了解Spring AOP的概念及使用

文章目录

  • 1. AOP概念
    • 1.1 什么是AOP?
    • 1.2 什么是Spring AOP?
  • 2. Spring AOP的使用
    • 2.1 引入Spring AOP依赖
    • 2.2 编写AOP程序
  • 3. Spring AOP详解
    • 3.1 Spring AOP核心概念
      • 1. 切点(Pointcut)
      • 2. 连接点(Join Point)
      • 3. 通知(Advice)
      • 4. 切面(Aspect)
    • 3.2 切⾯优先级 @Order

1. AOP概念

1.1 什么是AOP?

AOP全称Aspect Oriented Programming(⾯向切⾯编程),它和面向对象编程(OOP)是不同的,OOP是一种以对象为核心的编程思想,AOP是一种以切面为核心的编程思想
切面可以特指某一特定的问题,如日志记录、事务管理、安全性和错误处理等。
而AOP通过将横切关注点从应用程序的核心逻辑中分离出来,减少了代码冗余和复杂性。简单的来说,AOP就是一种思想,对某一类事情的集中处理。

1.2 什么是Spring AOP?

AOP是一种思想,那Spring AOP就是一种实现的方式,当然不是唯一的实现方式,还有AspectJ、CGLIB等。
当要进行网站用户是否登录判断,如图:
在这里插入图片描述
如果业务模块中只有一个业务,会有人说,这不是多此一举吗?是的,是多此一举,但是一个业务模块中往往会有着许多许多的业务,那么就可以看出来AOP的强大了,大大的降低代码的耦合性,并且会很好的维护。

2. Spring AOP的使用

直接介绍概念不免略显空洞,但Spring AOP使用起来却很简单,我们可以先使用一下,然后在介绍那令人头大的概念。

2.1 引入Spring AOP依赖

在创建项目时,无法自动引入Spring AOP依赖,只能手动复制到pom.xml文件中,如果你有代码可以直接复制,没有的话,也可以从Maven仓库中复制,直接搜索找到对应版本即可。
也可以复制我的:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.2 编写AOP程序

切面类UserAspect.java:

package com.example.springaopdemo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //告诉spring切面类
@Component
@Slf4j
public class UserAspect {
    //切点
    @Pointcut("execution(* com.example.springaopdemo.controller.UserController.*(..))")
    public void Pointcut(){

    }
    //前置通知
    @Before("Pointcut()")
    public void BeforeAdvice(){
        log.info("执行了前置通知");
    }
    //后置通知
    @After("Pointcut()")
    public void AfterAdvice(){
        log.info("执行了后置通知");
    }
    //环绕通知
    @Around("Pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("进入环绕通知");
        Object object = null;
        object = joinPoint.proceed();
        log.info("退出环绕通知");
        return object;
    }
}

业务类UserController.java:

package com.example.springaopdemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
@Slf4j
public class UserController {
    @RequestMapping("/sayHi")
    public String sayHi(){
        log.info("执行了sayHi方法  ");
        return "hi";
    }
    @RequestMapping("/sayHello")
    public String sayHello(){
        log.info("执行了sayHello方法  ");
        return "hello";
    }
}

参考业务类ArticleController.java:

package com.example.springaopdemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/art")
@Slf4j
public class ArticleController {
    @RequestMapping("/showArt")
    public String showArt(){
        log.info("执行了showArt方法");  
        return "100篇";
    }
}


访问user/sayHi:
在这里插入图片描述
访问user/sayHello:
在这里插入图片描述
访问art/showArt:
在这里插入图片描述
上面就是Spring AOP的简单使用,可以看到访问不同接口的结果不同,为什么前两个接口又可以调用切口类中的方法呢?那些注解的作用是什么?什么又是切口类?调用顺序为什么又是这样?下面慢慢介绍。

注解:

  1. @Aspect:告诉Spring这是一个切口类。
  2. @Pointcut:定义一个切点。里面的参数后面介绍。
  3. @Before:前置通知。目标方法前执行。
  4. @After:后置通知。目标方法后执行。
  5. @Around:环绕通知。在⽬标⽅法的前后都会被执⾏. 后⾯的表达式表⽰对哪些⽅法进⾏增强。

在这里插入图片描述

3. Spring AOP详解

3.1 Spring AOP核心概念

分为四部:

  1. 切点(Pointcut)。
  2. 连接点(Join Point)。
  3. 通知(Advice)。
  4. 切面(Aspect)。

其中切面中包括切点和通知。
切点包括连接点。

1. 切点(Pointcut)

在这里插入图片描述

切点又称切入点,里面定义了一组规则,告诉程序哪些方法需要进行功能增强。
使用@Pointcut注解,注解具有切点表达式,可以匹配访问修饰符,类,方法,参数等。
execution() 是最常⽤的切点表达式, ⽤来匹配⽅法, 语法为:

 execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

访问修饰符可以写public,也可以省略,也可以使用 * 替代,* 表示不做限制,也可以替代包,类,方法,参数,都表示任意的意思。
但是替代参数时,只能替代一个任意参数,而 . . 可以替代任意参数。
替代包时,也只能替代一层包,而 . . 可以替代任意层包。

这样就可以分析上面这段代码了,限制任意访问修饰符(省略了),* 任意返回类型,然后com.example.springaopdemo.controller包下的UserController类中的任意方法,任意参数可以调用这个切点。
这也就是,UserController类中的接口调用时,为和会打印了那些通知日志,而ArticleController类中的接口调用时没有的原因。

2. 连接点(Join Point)

满⾜切点表达式规则的⽅法, 就是连接点。也就是可以被AOP控制的⽅法
以程序举例, 所有 com.example.springaopdemo.controller.UserController 路径下的⽅法, 都是连接点。
连接点是满足切点表达式的方法,而切点可以看作所有连接点的集合。
比如:学校中,教数学的老师们,教数学就是切点表达式,教数学的老师都是一个连接点,教数学的所有老师就构成了切点。

3. 通知(Advice)

通知就是具体要做的工作,要统一解决的问题。在连接点处执行的代码逻辑,通知分为:

  1. 前置通知(Before):连接点方法执行前执行,一般没有返回值。
  2. 后置通知(After):连接点方法执行后执行,一般没有返回值。
  3. 环绕通知(Around):可以同时在方法调用前、调用后和返回时执行。有返回值。
  4. 返回通知(AfterReturning):在方法返回时执行。
  5. 异常通知(AfterThrowing):在方法抛出异常时执行。

所以上面接口调用时,打印的日志才是那种形式。
在AOP⾯向切⾯编程当中, 我们把这部分重复的代码逻辑抽取出来单独定义, 这部分代码就是通知的内容。

4. 切面(Aspect)

切面是一个普通的Java类,它包含了一个或多个通知方法。
切面通过使用Spring AOP的代理机制来将通知方法与目标对象的方法绑定在一起。
当目标对象的方法被调用时,相关的通知方法也会被执行。
切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)

3.2 切⾯优先级 @Order

当一个项目中定义了多个切面类,并且多个多个切面类的多个切入点都匹配到同一个目标方法,那么多个切面类的通知方法执行顺序是什么样的呢?
创建两个新的切面类:
UserAspect2.java:

package com.example.springaopdemo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //告诉spring切面类
@Component
@Slf4j
public class UserAspect2 {
    //切点
    @Pointcut("execution(* com.example.springaopdemo.controller.UserController.*(..))")
    public void Pointcut(){

    }
    @Before("Pointcut()")
    public void BeforeAdvice(){
        log.info("执行了前置通知->UserAspect2");
    }
    @After("Pointcut()")
    public void AfterAdvice(){
        log.info("执行了后置通知->UserAspect2");
    }
}

UserAspect3.java:

package com.example.springaopdemo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //告诉spring切面类
@Component
@Slf4j
public class UserAspect3 {
    //切点
    @Pointcut("execution(* com.example.springaopdemo.controller.UserController.*(..))")
    public void Pointcut(){

    }
    @Before("Pointcut()")
    public void BeforeAdvice(){
        log.info("执行了前置通知->UserAspect3");
    }
    @After("Pointcut()")
    public void AfterAdvice(){
        log.info("执行了后置通知->UserAspect3");
    }
}

调用一个接口:
在这里插入图片描述
根据结果看出,执行顺序是按照类型排序:

@Before 通知:字⺟排名靠前的先执⾏。
@After 通知:字⺟排名靠前的后执⾏。

这样有时并无法满足我们的需要,是否可以更好的控制执行顺序?
那么,就需要用到**@Order**注解。
在这里插入图片描述
再次调用刚才的接口:
在这里插入图片描述
根据结果看出,@Order 注解标识的切⾯类, 执⾏顺序如下:

@Before 通知:数字越⼩先执⾏。
@After 通知:数字越⼤先执⾏。

在这里插入图片描述


http://www.kler.cn/a/154321.html

相关文章:

  • 层归一化和批归一化
  • 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--完善TODO标记的代码
  • Vue2教程002:Vue指令
  • 【Ubuntu24.04】使用服务器
  • 卷径计算(基于卷径变化微分方程计算实时卷径)
  • Python数据分析:分组转换transform方法
  • LangChain 18 LangSmith监控评估Agent并创建对应的数据库
  • 生成带依赖Jar 包的两种常用方式:IDEA打包工具:Artifacts 和 maven-shade-plugin
  • Effective C++(四): 资源管理
  • 将不同时间点的登录状态记录转化为不同时间段的相同登录状态SQL求解
  • 【数据结构】树与二叉树(廿三):树和森林的遍历——层次遍历(LevelOrder)
  • 康托展开(Cantor Expansion)
  • 使用trigger-forward跨流水线传递参数
  • 腾讯云优惠券领取入口及使用指南
  • 运筹学-使用python建模基本操作
  • vscode中使用luaide-lite插件断点调试cocos2dx-lua
  • [传智杯 #2 决赛] 补刀
  • 【AIGC】接着昨天的AI“洗图”骚操作,继续调戏国产大模型
  • Android 应用资源概览
  • 蓝桥杯day03——Bigram 分词
  • 2023年12月03日新闻简报(国内国际)
  • 【送书活动三期】解决docker服务假死问题
  • 智能诊疗体验:整合AI技术的互联网医院小程序开发
  • LZW的编码和解码
  • AntDB“超融合+流式实时数仓”——打造分布式数据库新纪元
  • C语言结构体详解(一)(能看懂文字就能明白系列)