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

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 通知:数字越⼤先执⾏。

在这里插入图片描述

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是小辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值