当切点定义使用 private 修饰时,仅能在当前切面类中使用,当其他切面类也要使用当前切点定义时,就需要把 private 改为 public。引用方式为:全限定类名.方法名()。

@Slf4j
@Component
@Aspect
public class TestAspect2 {
    @Before("com.example.demo.aspect.TestAspect.pt()")
    public void doBefore() {
        log.info("执⾏ TestAspect2 -> Before ⽅法");
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

3.4 切面优先级 @Order:

当我们在一个项目中,定义了多个切面类时,并且这些切面类的多个切入点都匹配到了同一个目标方法。当目标方法运行的时候,这些切面类中的通知方法都会执行,那么这几个通知方法的执行顺序是什么样的呢?

我们通过程序来进行验证。

@Slf4j
@Component
@Aspect
public class TestAspect2 {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("执行 TestAspect2 -> Before 方法");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("执行 TestAspect2 -> After 方法");
    }
}

@Aspect
@Component
@Slf4j
public class TestAspect3 {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("执行 TestAspect3 -> Before 方法");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("执行 TestAspect3 -> After 方法");
    }
}

@Aspect
@Component
@Slf4j
public class TestAspect4 {
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("执行 TestAspect4 -> Before 方法");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("执行 TestAspect4 -> After 方法");
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

运行上面程序:

image-20241011084037226

通过上述程序的运行结果,可以看出:

存在多个切面类时,默认按照切面类的类名字母排序:

但这种方式不方便管理,我们的类名更多还是具备一定含义的。

Spring 给我们提供了一个新的注解,来控制这些切面通知的执行顺序:@Order。

@Slf4j
@Component
@Aspect
@Order(10)
public class TestAspect2 {
    //代码省略
}

@Aspect
@Component
@Slf4j
@Order(5)
public class TestAspect3 {
    //代码省略
}

@Aspect
@Component
@Slf4j
@Order(1)
public class TestAspect4 {
    //代码省略
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

运行程序:

image-20241011084529703

通过上述程序的运行结果,得出结论:

@Order 注解标识的切面类,执行顺序如下:

@Order 的执行顺序可以抽象成下面这张图:

image-20241011084901893

3.5 切点表达式:

上面的代码中,我们一直在使用切点表达式来描述切点。下面我们来介绍一下切点表达式的语法。

切点表达式常见有两种表达方式:

  1. execution:根据方法的签名来匹配。
  2. @annotation:根据注解匹配。

3.5.1 execution 表达式:

execution() 是最常用的切点表达式,用来匹配方法,语法为:

execution (<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

其中:访问修饰符和异常可以省略。

image-20241011085755735

切点表达式支持通配符表达:

  1. 包名使用 * 表示任意包(一层包使用一个 * )。
  2. 类名使用 * 表示任意类。
  3. 返回值使用 * 表示任意返回值类型。
  4. 方法名使用 * 表示任意方法(参数可能有限制)。
  5. 参数使用 * 表示一个任意类型的参数。
  1. 使用..配置包名,标识此包以及此包下的所有子包。
  2. 可以使用..配置参数,任意个任意类型的参数。

3.5.2 @annotation:

execution 表达式更适用有规则的,如果我们要匹配多个无规则的方法呢,比如:TestController 中的 t1() 和 UserController 中的 u1() 这两个方法。这个时候我们使用 execution 这种切点表达式来描述就不是很方便了。我们可以借助自定义注解的方式以及另一种切点表达式 @annotation 来描述这一类的切点。

实现步骤:

  1. 编写自定义注解。
  2. 使用 @annotation 表达式来描述切点。
  3. 在方法上添加自定义注解。

创建一个注解类(和创建 Class 文件一样的流程,选择 Annotation 就可以了)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

使用 @annotation 表达式来描述切点。

@Component
@Slf4j
@Aspect
public class MyAspectDemo {
    @Before("@annotation(com.example.demo.aspect.MyAspect)")
    public void doBefore(){
        log.info("我是 MyAspectDemo");
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在方法上添加自定义注解。

@RequestMapping("/test")
@RestController
@Slf4j
public class TestController {
    @MyAspect
    @RequestMapping("/t1")
    public void test1(){
        log.info("我是 test1");
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

运行程序,访问 test1 方法。

image-20241011093318331

3.6 Spring AOP 的实现方式(常见面试题):

  1. 基于注解 @Aspect。

  2. 基于自定义注解(@annotation)。

  3. 基于 Spring API(通过 xml 配置的方式,自从 SpringBoot 广泛使用之后,这种方法几乎看不到了)。

  4. 基于代理来实现(更加久远的一种实现方式,写法笨重,不建议使用)。

四、代理模式

Spring AOP 是基于动态代理来实现 AOP 的。

代理模式,也叫委托模式。

定义:

为其他对象提供一种代理,以控制对这个对象的访问。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。

image-20241014222048190

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

根据代理的创建时期,代理模式分为静态代理动态代理

结语:
其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话,还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

在这里插入图片描述

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/2301_80035594/article/details/143078123","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的gobeyye的文章"https://blog.csdn.net/2301_80035594/article/details/143078123"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!