Spring AOP 实践指南

news2024/12/26 23:15:59

Spring AOP 实践指南

文章目录

  • Spring AOP 实践指南
  • 一、概述
    • 1、简介
    • 2、官方资料
    • 3、本文档说明
  • 二、基本使用
    • 1、引入依赖
    • 2、定义切面
    • 3、定义切点
    • 4、创建 `HelloController`
    • 5、启动项目,访问测试
  • 三、通知
    • 1、概述
      • 五种通知
      • 通知的顺序
    • 2、通知方法接受的参数
    • 3、前置通知
      • 代码实现
      • 控制台打印内容
    • 4、后置通知
      • 代码实现
      • 控制台打印内容
    • 5、环绕通知
      • 代码实现
      • 控制台打印内容
    • 6、最终通知
      • 代码实现
      • 控制台打印内容
    • 7、异常通知
      • 代码实现
      • 修改 `HelloController` 制造异常
      • 控制台打印内容
  • 四、切点表达式
    • 1、概述
      • 概述
      • 通配符
    • 2、execution
      • 简介
      • 语法
      • 解释
      • 示例
    • 3、within
      • 简介
      • 语法
      • 示例
      • 注意
    • 4、this
      • 简介
      • 语法
      • 示例
      • 注意
    • 5、target
      • 简介
      • 语法
      • 示例
      • 注意
    • 6、args
      • 简介
      • 语法
      • 示例
      • 注意
    • 7、bean
      • 简介
      • 语法
      • 示例
      • 注意
    • 8、@within
      • 简介
      • 语法
      • 示例
      • 具体代码示例
    • 9、@target
      • 简介
      • 语法
      • 示例
      • 注意
    • 10、@annotation
      • 简介
      • 语法
        • 示例
      • 注意
    • 11、@args
      • 简介
      • 语法
      • 示例
      • 注意
  • 五、切点表达式组合
    • 1、概述
    • 2、示例
  • 六、注意事项

一、概述

1、简介

Spring AOP(面向切面编程)是Spring框架的一个关键特性之一。它提供了一种在应用程序中实现横切关注点的方法,这些关注点通常会散布在应用程序的多个模块中,并且与核心业务逻辑存在交叉。

AOP通过将关注点从它们所影响的对象中分离出来,使得开发人员能够更好地关注业务逻辑的实现,而不必担心与之交织在一起的横切关注点。在Spring中,这些横切关注点可以包括日志记录、安全性、事务管理、性能监控等等。

Spring AOP的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和引入(Introduction)。

  • 切面(Aspect):切面是一个模块化单元,它是横切关注点的实现。它由切点和通知组成。
  • 连接点(Join Point):连接点是在应用程序执行期间可以插入切面的点。例如,方法调用、方法执行、异常处理等都可以是连接点。
  • 通知(Advice):通知是在连接点上执行的代码。它定义了在何时、何地和如何应用切面。常见的通知类型包括前置通知(Before)、后置通知(After)、返回通知(After Returning)和异常通知(After Throwing)。
  • 切点(Pointcut):切点定义了在何处应用通知。通过使用切点表达式,可以指定连接点的匹配规则。
  • 引入(Introduction):引入允许我们向现有的类添加新的接口和实现,以便可以将新功能引入到这些类中。

Spring AOP使用代理模式来实现横切关注点的管理。在运行时,Spring会动态地创建代理对象,将通知织入到目标对象的方法调用中。

通过使用Spring AOP,开发人员可以更好地实现关注点的模块化和重用,从而提高代码的可维护性和可扩展性。

2、官方资料

Spring 官网:https://spring.io/

Spring 文档:https://docs.spring.io/spring-framework/reference/

3、本文档说明

本文档基于 Spring Boot 以及注解使用 Spring AOP 功能。

二、基本使用

1、引入依赖

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

2、定义切面

在 Spring 管理的 Bean 类上使用 @Aspect 注解就可以定义一个切面。

package com.example.demo.aspects;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {
}

3、定义切点

在切面类的方法使用 @Pointcut 注解来定义切点,然后在通知注解中使用方法签名来指定切点。

切点表达式用来匹配切入的目标类和方法。目标类只能是 Spring 容器管理的类,切面只能切入 Bean 中的方法。

package com.example.demo.aspects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param pjp 切点
     * @return Object
     * @throws Throwable 异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("before");
        Object result = pjp.proceed();
        System.out.println("after");
        return result;
    }
}

4、创建 HelloController

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zibo
 * @date 2023/5/15 12:55
 * @slogan 真正的大师永远怀着一颗学徒的心。——易大师
 */
@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/world")
    public String helloWorld() {
        System.out.println("hello world");
        return "Hello World!";
    }

}

5、启动项目,访问测试

访问地址:http://localhost:8080/hello/world

# 响应结果
Hello World!

# 控制台
before
Hello World!
after

三、通知

1、概述

五种通知

AOP 中的通知是基于连接点(Join point)业务逻辑的一种增强,Spring AOP 提供了下面五种通知类型:

  • Before advice(前置通知):连接点前面执行,不能终止后续流程,除非抛异常
  • After returning advice(后置通知):连接点正常返回时执行,有异常不执行
  • Around advice(环绕通知):围绕连接点前后执行,也能捕获异常处理
  • After advice(最终通知):连接点退出时执行,无论是正常退出还是异常退出
  • After throwing advice(异常通知):连接点方法抛出异常时执行

AOP 的连接点一般是指目标类的方法,五种通知类型执行的节点如下:

通知的顺序

Spring AOP 中一个目标类可以被多个切面切入,多个切面也可以切入一个目标类。

使用 @Order 注解来指定切面的优先级,来控制切面的执行顺序。

在注册切面 Bean 的时候指定 @Order,如下:

@Order(1)
@Aspect
@Component
public class FirstAspect {
    // ......
}

2、通知方法接受的参数

  1. JoinPoint:JoinPoint是Spring AOP中表示连接点的对象。它提供了访问连接点的信息,如目标对象、方法签名、方法参数等。通过JoinPoint参数,可以获取有关当前正在执行的连接点的信息。
  2. ProceedingJoinPoint:ProceedingJoinPoint是JoinPoint的一个子接口,它只在使用环绕通知时才会使用。它提供了proceed()方法,用于执行连接点方法。ProceedingJoinPoint参数可以用于在环绕通知中控制连接点方法的执行。
  3. org.aspectj.lang.JoinPoint.StaticPart:JoinPoint.StaticPart表示连接点的静态部分。它提供了与JoinPoint相同的信息,但不提供对连接点方法的执行控制。
  4. org.aspectj.lang.Signature:Signature表示连接点方法的签名。它提供了对方法名称、修饰符、返回类型、参数类型等的访问。
  5. org.aspectj.lang.ProceedingJoinPoint.StaticPart:ProceedingJoinPoint.StaticPart是ProceedingJoinPoint的静态部分。它提供了与ProceedingJoinPoint相同的信息,但不提供对连接点方法的执行控制。
  6. org.aspectj.lang.JoinPoint.EnclosingStaticPart:JoinPoint.EnclosingStaticPart表示连接点所在的静态部分。它提供了与JoinPoint相同的信息,但是可以用于获取连接点所在的类或切面的信息。

这些参数可以根据需要选择性地在通知方法中使用,以获取关于连接点和方法的相关信息。

3、前置通知

代码实现

package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 前置通知
     *
     * @param jp 切点
     */
    @Before("pointcut()")
    public void beforeAdvice(JoinPoint jp) {
        System.out.println("前置通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
    }
}

控制台打印内容

前置通知
方法名称:helloWorld
hello world # 此句的方法内部打印内容

4、后置通知

代码实现

package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 后置通知
     *
     * @param jp 切点
     * @param result 返回值
     */
    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint jp, Object result) {
        System.out.println("后置通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
        // 获取返回值
        System.out.println("返回值:" + result);
    }

}

控制台打印内容

hello world # 此句的方法内部打印内容
后置通知
方法名称:helloWorld
返回值:Hello World!

5、环绕通知

代码实现

package com.example.demo.aspects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param proceedingJoinPoint 连接点
     * @return 连接点方法的返回值
     * @throws Throwable 可能抛出的异常
     */
    @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知 - 前置逻辑");
        // 获取方法名称
        String methodName = proceedingJoinPoint.getSignature().getName();
        System.out.println("方法名称:" + methodName);

        // 执行连接点方法
        Object result = proceedingJoinPoint.proceed();

        System.out.println("环绕通知 - 后置逻辑");
        // 可以对返回值进行处理或修改
        System.out.println("返回值:" + result);

        return result;
    }

}

控制台打印内容

环绕通知 - 前置逻辑
方法名称:helloWorld
hello world # 此句的方法内部打印内容
环绕通知 - 后置逻辑
返回值:Hello World!

6、最终通知

代码实现

package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 最终通知
     *
     * @param jp 切点
     */
    @After("pointcut()")
    public void afterAdvice(JoinPoint jp) {
        System.out.println("最终通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
    }

}

控制台打印内容

hello world # 此句的方法内部打印内容
最终通知
方法名称:helloWorld

7、异常通知

代码实现

package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 异常通知
     *
     * @param jp        切点
     * @param exception 异常对象
     */
    @AfterThrowing(pointcut = "pointcut()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint jp, Exception exception) {
        System.out.println("异常通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
        // 获取异常信息
        System.out.println("异常信息:" + exception.getMessage());
    }

}

修改 HelloController 制造异常

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zibo
 * @date 2023/5/15 12:55
 * @slogan 真正的大师永远怀着一颗学徒的心。——易大师
 */
@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/world")
    public String helloWorld() {
        System.out.println("hello world");
        int num = 10 / 0;
        return "Hello World!";
    }

}

控制台打印内容

hello world # 此句的方法内部打印内容
异常通知
方法名称:helloWorld
异常信息:/ by zero

四、切点表达式

1、概述

概述

切入点指示符用来指示切入点表达式目的,在 Spring AOP 中目前只有执行方法这一个连接点,Spring AOP 支持的 AspectJ 切入点指示符,切入点表达式可以使用 &&、||、!来组合切入点表达式,还可以使用类型匹配的通配符来进行匹配。

通配符

类型匹配通配符说明
*表示匹配任何数量字符。示例:java.*.String,表示匹配 java 包下的任何"一级子包"下的 String 类型; 如匹配 java.lang.String,但不匹配java.lang.ss.String
表示任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。示例:java…* ,表示匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation
+仅能作为后缀放在类型模式后边,匹配指定类型的子类型;

2、execution

简介

execution 切点表达式用于定义切点的匹配规则,根据方法的修饰符、返回类型、方法名、参数类型和异常类型等来进行匹配。

语法

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

解释

  • modifiers-pattern:方法的修饰符,如 publicprivate 等。可选。
  • ret-type-pattern:方法的返回类型。使用 * 表示任意返回类型,使用完全限定的类名表示具体的返回类型。必选。
  • declaring-type-pattern:方法所在的类或接口。使用完全限定的类名表示具体的类或接口。可选。
  • name-pattern:方法名,使用 * 表示任意方法名。必选。
  • param-pattern:方法的参数类型。使用 * 表示任意参数类型,使用完全限定的类名表示具体的参数类型。可选。
  • throws-pattern:方法抛出的异常类型。使用完全限定的类名表示具体的异常类型。可选。

示例

匹配 public 方法:

execution(public * *(..))

匹配名称以 set 开头的方法:

execution(* set*(..))

匹配指定类的所有方法:

execution(* com.example.demo.service.UserService.*(..))

匹配指定包及其子包下的类或接口的所有方法:

execution(* com.example.demo.service..*(..))

匹配带有特定注解的方法:

execution(@com.example.demo.annotation.Loggable * *(..))

匹配返回类型为指定类型的方法:

execution(java.util.List<com.example.demo.model.User> com.example.demo.service.UserService.*(..))

这些示例展示了不同的 execution 切点表达式的用法,你可以根据具体的需求和要匹配的方法特征来定义切点表达式。

3、within

简介

within 切点表达式用于定义切点的作用范围,根据类型(类或接口)来匹配其中的方法执行。

语法

within(type-pattern)

示例

匹配指定类中的所有方法:

within(com.example.demo.service.UserService)

匹配指定包及其子包下的所有类或接口的方法:

within(com.example.demo.service..*)

匹配指定包中的所有类或接口的方法:

within(com.example.demo.service.*)

匹配指定包及其子包下的所有类的所有方法:

within(com.example.demo..*)

这些示例展示了使用 within 切点表达式的一些常见用法,你可以根据具体的需求和要匹配的类型来定义切点表达式。

注意

需要注意的是,within 切点表达式只能匹配到类型级别,无法直接匹配到具体的方法。

4、this

简介

this 切点表达式用于匹配当前代理对象所实现的接口类型,并选择这些接口中定义的方法作为切点。

语法

this(type)

示例

这个示例表示匹配当前代理对象所实现的 com.example.demo.service.UserService 接口中定义的所有方法。

this(com.example.demo.service.UserService)

注意

需要注意的是,this 切点表达式只能匹配到当前代理对象实现的接口方法,并不包括其实现类或其他接口的方法。

5、target

简介

target 切点表达式用于匹配目标对象的类型,并选择这些类型中定义的方法作为切点。

语法

target(type)

示例

这个示例表示匹配目标对象的类型为 com.example.demo.service.UserService,即选择目标对象为该类型的所有方法作为切点。

target(com.example.demo.service.UserService)

注意

需要注意的是,target 切点表达式匹配的是目标对象的类型,而不是当前代理对象的类型。这意味着它会选择目标对象的方法,而不考虑当前代理对象的实现类或其他接口的方法。

6、args

简介

args 切点表达式用于匹配方法的参数类型,并选择具有匹配参数类型的方法作为切点。

语法

args(type-pattern)

示例

匹配带有一个整型参数的方法:

args(int)

匹配带有任意参数类型的方法:

args(*)

匹配带有一个字符串参数的方法:

args(java.lang.String)

匹配带有两个整型参数的方法:

args(int, int)

注意

需要注意的是,args 切点表达式仅匹配参数类型,而不考虑参数名称。它只选择具有匹配参数类型的方法,而不限制参数的个数或顺序。

7、bean

简介

bean 切点表达式用于匹配 Spring 容器中的 Bean 名称,并选择具有匹配名称的 Bean 的方法作为切点。

语法

bean(beanNamePattern)

示例

匹配名称为 “userService” 的 Bean:

bean(userService)

匹配名称以 “service” 结尾的 Bean:

bean(*Service)

匹配名称以 “service” 开头并且包含 “impl” 的 Bean:

bean(service*impl)

注意

需要注意的是,bean 切点表达式匹配的是 Spring 容器中的 Bean 名称,而不是具体的类名或接口名。

8、@within

简介

@within 切点表达式用于匹配被特定注解标注的类及其子类中定义的方法作为切点。

语法

@within(annotation-type)

示例

@within(org.springframework.stereotype.Service)

这个示例表示匹配被 @Service 注解标注的类及其子类中定义的所有方法作为切点。

具体代码示例

package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 异常通知
     *
     * @param jp 切点
     */
    @Before("@within(service)")
    public void beforeAdvice(JoinPoint jp, Service service) {
        System.out.println("前置通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
        // 获取注解的值
        String value = service.value();
        System.out.println("注解的值:" + value);
    }

}

9、@target

简介

@target 切点表达式用于匹配目标对象所属的类上标注的注解类型,并选择这些类中定义的方法作为切点。

语法

@target(annotation-type)

示例

这个示例表示匹配目标对象所属的类上标注了 @Service 注解的所有方法作为切点。

@target(org.springframework.stereotype.Service)

注意

需要注意的是,@target 切点表达式匹配的是目标对象所属的类上的注解,而不是当前代理对象所属的类上的注解。它会选择目标对象所属的类中定义的方法,而不考虑当前代理对象的实现类或其他接口的方法。

10、@annotation

简介

@annotation 切点表达式用于匹配被特定注解标注的方法,并选择这些方法作为切点。

语法

@annotation(annotation-type)

示例

这个示例表示匹配被 @RequestMapping 注解标注的方法作为切点。

@annotation(org.springframework.web.bind.annotation.RequestMapping)

注意

需要注意的是,@annotation 切点表达式匹配的是方法上的注解,而不是类级别的注解。它会选择被注解标注的方法,而不包括其他方法或类级别的注解。

11、@args

简介

@args 切点表达式用于匹配方法参数上具有特定注解的方法,并选择这些方法作为切点。

语法

@args(annotation-type)

示例

这个示例表示匹配方法参数上具有 @PathVariable 注解的方法作为切点。

@args(org.springframework.web.bind.annotation.PathVariable)

注意

需要注意的是,@args 切点表达式匹配的是方法参数上的注解,而不是方法本身或类级别的注解。它会选择具有特定注解的方法,而不包括其他方法或类级别的注解。

五、切点表达式组合

1、概述

切点表达式的组合可以使用逻辑运算符 &&(与)、||(或)、!(非)来组合多个切点表达式。括号可以用于明确定义优先级和逻辑关系。

2、示例

使用逻辑运算符 &&(与):

execution(public * com.example.service.*Service.*(..)) && @within(org.springframework.stereotype.Service)

该示例表示匹配包名为 com.example.service 的类中,被 @Service 注解标注的方法。

使用逻辑运算符 ||(或):

execution(public * com.example.controller.*Controller.*(..)) || execution(public * com.example.service.*Service.*(..))

该示例表示匹配包名为 com.example.controller 的类中的方法或者包名为 com.example.service 的类中的方法。

使用逻辑运算符 !(非):

!execution(public void com.example.controller.AdminController.logout())

该示例表示匹配除了 com.example.controller.AdminController 类中的 logout 方法之外的所有方法。

使用括号进行优先级和逻辑关系的定义:

(execution(public * com.example.service.*Service.*(..)) && @within(org.springframework.stereotype.Service)) || execution(public * com.example.controller.*Controller.*(..))

该示例表示匹配被 @Service 注解标注的 com.example.service 包中的类的方法,以及匹配 com.example.controller 包中的类的方法。

六、注意事项

  1. AOP代理:
    • Spring AOP 使用代理来实现切面的织入。默认情况下,Spring AOP 使用基于代理的 AOP,其中代理对象包装了目标对象,并在方法调用时执行切面逻辑。代理可以通过 JDK 动态代理或 CGLIB 生成。
    • 注意选择正确的代理模式,例如,当目标对象实现接口时,使用 JDK 动态代理,否则使用 CGLIB 代理。
  2. 切面优先级:
    • 多个切面可以应用于同一个方法或类,因此需要了解切面的优先级。可以通过实现 Ordered 接口或使用 @Order 注解来指定切面的执行顺序。
  3. 切点匹配性能:
    • 切点表达式的复杂度会影响性能。过于复杂的切点表达式可能导致切面执行的性能下降。尽量使用简单且高效的切点表达式,避免不必要的复杂性。
  4. 环绕通知的注意事项:
    • 环绕通知是最强大的通知类型,可以完全控制目标方法的执行。但在使用环绕通知时,需要确保调用 ProceedingJoinPoint 对象的 proceed() 方法,以继续执行目标方法。否则,目标方法将被阻止执行。
  5. 异常处理:
    • 当目标方法抛出异常时,可以使用异常通知来捕获和处理异常。异常通知可以帮助你在方法发生异常时执行额外的逻辑。
  6. Spring AOP 的限制:
    • Spring AOP 只能应用于 Spring 容器管理的 bean 上。它无法拦截自调用的方法、静态方法或无法通过 Spring 容器管理的对象。
  7. Proxy vs. AspectJ:
    • Spring AOP 提供了一种简化的方式来实现切面编程,但其功能相对有限。如果需要更高级的切面功能,例如在构造器、私有方法上织入切面,或者更细粒度的控制,可以考虑使用 AspectJ,它提供了更丰富和灵活的切面编程功能。

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

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

相关文章

Thread线程学习(1) 了解线程的基本知识——什么是线程

本专栏将记录有关线程方面的知识 在计算机科学领域中&#xff0c;线程&#xff08;Thread&#xff09;是一种执行计算机程序的基本单元。对于初学者来说&#xff0c;理解线程是学习并发编程的关键一步。本文将带你了解线程的基础知识&#xff0c;包括线程的定义、线程与进程的关…

GPT神奇应用:给孩子做每日安排

正文共 1163 字&#xff0c;阅读大约需要 4 分钟 家长必备技巧&#xff0c;您将在4分钟后获得以下超能力&#xff1a; 快速生成每日安排计划 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图片…

JavaScript实现打印倒金字塔的代码

以下为实现打印倒金字塔的程序代码和运行截图 目录 前言 一、实现打印倒金字塔 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题目要求实现相关使用功…

动态规划专题

动态规划专题 最长递增子序列LeetCode 300. 最长递增子序列解题思路代码实现 LeetCode 354. 俄罗斯套娃信封问题解题思路代码实现 总结 不要纠结&#xff0c;干就完事了&#xff0c;熟练度很重要&#xff01;&#xff01;&#xff01;多练习&#xff0c;多总结&#xff01;&…

【Redis】Redis中bitmap的原理和使用

文章目录 一、原理二、BitMap 相关命令三、BitMap 空间计算四、使用场景1. 用户签到2. 统计活跃用户&#xff08;用户登陆情况&#xff09;3. 统计用户在线状态4. 实现布隆过滤器 五、总结 一、原理 先声明一下&#xff1a;Redis 有5种数据类型&#xff0c;而 BitMap 在 Redis…

【k8s】Ubuntu22.04离线部署k8s集群:搭建软件仓库和镜像仓库(repo节点)

上两篇主要记录了在CentOS 7环境中离线部署k8s的方案&#xff0c;本篇继续介绍方案二在Ubuntu 22.04的实现。&#xff08;当然&#xff0c;整体思路还是跟上篇基本相似&#xff09; 目录 Ubuntu22.04离线部署k8s集群&#xff1a;搭建软件仓库和镜像仓库&#xff08;repo节点&am…

总结852

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;张宇强化前10讲&#xff0c;背诵15篇短文&#xff0c;熟词僻义300词基础词&#xff09; 周目标&#xff1a;张宇强化前5讲并完成相应的习题并记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#…

云上高校导航 小程序 开发教程 1.0.1

​ Gitee仓库&#xff1a;云上高校导航 GitHub仓库&#xff1a;云上高校导航 “云上高校导航”是一套基于小程序云开发的校园导航类系统开发方案&#xff0c;该开发方案可供开发者进行二次开发&#xff0c;用于解决师生和访客的校园出行需求。 项目优势及创新&#xff1a;…

ChatGPT vs. Bing vs. Bard

随着 2022 年 ChatGTP 的推出&#xff0c;人工智能聊天机器人的世界突然走上了一条新道路。如今&#xff0c;密切关注 AI 的人都知道&#xff0c;不同公司推出了几款产品。从谷歌拥有自己的 Bard AI&#xff0c;到微软发布新的 Bing AI Chat&#xff0c;再到 OpenAI 发布GPT-4。…

云服务器搭建Python项目实现学术优化chatgpt

云服务器搭建实现学术优化chatgpt 1 服务器准备2 云服务器配置2.1 python虚拟环境2.1.1 python3.9安装配置2.1.2 下载python项目2.1.3 创建python虚拟环境 3 后台运行python项目&#xff08;不然不能关闭与云服务器的连接&#xff0c;那意义何在&#xff1f;&#xff09; 1 服务…

GEE:将年度NDVI时间序列影像集合(Image Collection)转变为多波段影像,并下载

作者:CSDN @ _养乐多_ 本文将重点介绍如何使用 Google Earth Engine (GEE) 将多波段影像堆叠并导出,并探讨其应用场景和好处。 通过使用 GEE 的多波段影像堆叠功能,我们可以将不同波段的遥感影像整合成一个多波段影像,以支持各种地理空间分析任务。这种方法适用于遥感影…

以太网端口类型

以太网端口有 3种链路类型:access、trunk、hybird Access类型端口只能属于1个VLAN 般用于连接计算机 端口&#xff1b;Trunk类型端口可以允许多个VLAN通过,可以接收和发送多个VLAN 报文,一般用于交换机之间的连接&#xff1b;Hybrid类型端口可以允许多个VLAN通过&#xff0c;可…

Dubbo的使用

Dubbo在开发中&#xff0c;存在两种开发思路。基于SOA(面向服务的体系架构)思想和辅助SpringCloud架构提升效率。 Dubbo 默认使用 Netty 框架 Dubbo基于SOA思想 正常SpringBoot项目是只有一个启动类&#xff0c;接口定义在web层(即Controller层)中,然后调用Service层。&…

Matlab 梯度下降法

一、简介 梯度下降法&#xff08;Gradient descent&#xff09;或最速下降法&#xff08;steepest descent&#xff09;是求解无约束最优化问题的一种常用方法。 假设fx)在R上具有一阶连续偏导数的函数。要求解的无约束最优化问题是。其本质是一个迭代的方法&#xff0c;选择…

VMware Workstation 网络备忘 + 集群规模

概述 在虚拟机中部署服务&#xff0c;进行IP规划&#xff0c;进行相关的前期准备 3 张网卡 2个不同的网段 1个NAT 概述截图 NAT 截图 VMnet0 截图 VMnet1 截图 总结&#xff1a; 网卡&#xff08;网络适配器&#xff09;名称IP网段备注NATens33192.168.139.0VMnet0ens34VMne…

手把手教你在RT-THREAD bsp上运行pikascript脚本点亮小灯

简介 这篇文章介绍如何在RT-THREAD bsp上运行pikascript脚本。 pikascript相当于一个小型的micropython。 最近有一些结构上的调整&#xff0c;写这篇文章大概介绍一下如何使用&#xff0c;以及开发过程中需要注意的问题。 这篇文章几乎适配所有的RT-THREAD上的bsp。&#xff…

在Kubernetes(K8S) 上运行第一个应用

1、启动代理 &#xff1a; kubectl proxy 2、部署应用程序最简单的方式是使用 kubectl run 命令&#xff0c;该命令可以创建所有必要的组件而无需JSON或YAML文件。 --imageluksa/kubia 显示的是指定要运行的容器镜像&#xff0c;--port8080 选项告诉Kubernetes应用正在监听808…

每日一算-冒泡排序

冒泡排序是最简单的排序算法&#xff0c;如果相邻元素的顺序错误&#xff0c;则通过重复交换它们来工作。该算法不适用于大数据集&#xff0c;因为它的平均和最坏情况时间复杂度都很高。 原理 输入&#xff1a; arr[] {6, 3, 0, 5} 第一步&#xff1a; 冒泡排序从最前面的两个…

每日一算-选择排序算法

大家好&#xff0c;我是易安&#xff01; 今天我们开始每日一算的篇章&#xff0c;今天带来的是选择算法。 选择排序是一种简单而高效的排序算法&#xff0c;它通过从列表的未排序部分中重复选择最小&#xff08;或最大&#xff09;元素并将其移动到列表的已排序部分来工作。该…

Kubernetes集群安全加固

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/130678814 一、系统账户加固 1、对账户的登录次数进行检查&#xff0c;连续超过3次登录失败后&#xff0c;对用户锁定150s # 每个设备上都运行 sed -i~ 2iauth required pam_faillock.so deny3 unloc…