springboot提升-切面编程

开发步骤

在 Spring Boot 中使用切面(Aspect)开发可以帮助你在不修改现有业务逻辑的情况下,增加诸如日志记录、安全检查、性能监控等功能。Spring 提供了 Spring AOP(面向切面编程)的支持,它允许你定义切面来封装横切关注点(cross-cutting concerns),并通过通知(advice)来增强程序的功能。

以下是如何在 Spring Boot 中使用切面开发的基本步骤:

1. 引入依赖

确保你的项目包含 Spring AOP 的依赖。如果你使用的是 Spring Boot,则默认已经包含了 Spring AOP 的支持,因此无需额外添加依赖。

2. 创建切面类

创建一个切面类,并使用 @Aspect 注解标记它。在这个类中定义通知(Advice)方法,这些方法将在特定的连接点(join point)执行。

示例切面类:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void loggingPointcut() {
        // 无具体实现,仅作为切入点定义
    }

    @Before("loggingPointcut()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Entering method: {}.{} with arguments: {}",
                joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName(),
                joinPoint.getArgs());
    }

    @After("loggingPointcut()")
    public void logAfter(JoinPoint joinPoint) {
        logger.info("Exiting method: {}.{}",
                joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName());
    }
}

在这个例子中,我们定义了一个名为 loggingPointcut 的切入点,它匹配 com.example.service 包下所有类的所有方法。logBefore 方法会在切入点匹配的方法执行前被调用,而 logAfter 方法则在方法执行后被调用。

3. 定义切入点表达式

在切面类中定义切入点表达式(pointcut expression),它可以指定何时执行通知。Spring 支持多种类型的切入点表达式,包括但不限于方法执行、异常抛出等。

示例切入点表达式:
@Pointcut("execution(* com.example.service.*.*(..))")
public void loggingPointcut() {
    // 无具体实现,仅作为切入点定义
}

这个表达式定义了一个名为 loggingPointcut 的切入点,它匹配 com.example.service 包下所有类的所有方法。

4. 使用注解定义通知

使用 @Before@After@Around@AfterReturning@AfterThrowing 等注解来定义不同类型的通知。

示例通知:
@Before("loggingPointcut()")
public void logBefore(JoinPoint joinPoint) {
    // 在切入点匹配的方法执行前的操作
}

@After("loggingPointcut()")
public void logAfter(JoinPoint joinPoint) {
    // 在切入点匹配的方法执行后的操作
}

5. 自动装配切面类

在 Spring Boot 中,由于使用了组件扫描 (@ComponentScan),所以只需要将切面类标注为 @Component,Spring 就会自动检测并装配它们。如果你没有使用组件扫描,也可以显式地在配置类中声明切面。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

6. 测试切面功能

编写单元测试来验证切面是否按预期工作。你可以编写一些测试用例来触发被切面增强的方法,并检查日志输出是否符合预期。

注意事项

  • 确保切入点表达式正确无误,否则通知可能不会在预期的位置执行。
  • 谨慎使用 @Around 通知,因为它可以完全控制方法的执行流程,容易引起死锁或其他问题。
  • 如果有多个切面,需要注意它们之间的顺序,可以通过 Ordered 接口来控制顺序。

通过以上步骤,你可以在 Spring Boot 中成功地实现切面开发,从而在不侵入现有业务代码的情况下增加新的功能。

切面注解

在 Spring Boot 中,使用切面(Aspect)来进行面向切面编程(AOP)可以通过一系列注解来定义和配置切面的行为。以下是 Spring AOP 中常用的几个注解及其用途:

常用的切面注解

  1. @Aspect

    • 用于标记一个类是一个切面类。
    • 这个类将包含多个通知(Advice)方法,这些方法会在特定的连接点(Join Point)处被调用。
  2. @Component

    • 将切面类注册为 Spring Bean,确保切面类能够被 Spring 容器管理。
    • 通常与 @Aspect 结合使用。
  3. @Before

    • 用于在目标方法执行前执行通知方法。
    • 如果 @Before 通知方法抛出了异常,则不会执行目标方法。
  4. @After

    • 用于在目标方法执行后执行通知方法,无论方法是否成功执行或抛出异常。
    • 这个通知总是会被执行,除非它自己抛出了一个未处理的异常。
  5. @AfterReturning

    • 用于在目标方法成功完成后执行通知方法。
    • 如果目标方法抛出了异常,则此通知不会被执行。
  6. @AfterThrowing

    • 用于在目标方法抛出异常后执行通知方法。
    • 只有当目标方法抛出异常时,此通知才会被执行。
  7. @Around

    • 用于在目标方法执行前后都执行通知方法,可以用来实现环绕通知,如性能监视等。
    • 这种通知可以决定是否继续执行目标方法或直接返回或抛出异常。

示例代码

下面是一个简单的示例,展示了如何使用这些注解来定义一个切面:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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 LoggingAspect {

    // 定义一个切入点表达式,用于匹配特定的方法调用
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceLayerExecution() {}

    // 在切入点处执行前置通知
    @Before("serviceLayerExecution()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Executing @Before advice on method: " + joinPoint.getSignature());
    }

    // 在切入点处执行后置通知
    @After("serviceLayerExecution()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("Executing @After advice on method: " + joinPoint.getSignature());
    }

    // 在切入点处执行返回通知
    @AfterReturning(pointcut = "serviceLayerExecution()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("Executing @AfterReturning advice on method: " + joinPoint.getSignature() + ", result: " + result);
    }

    // 在切入点处执行异常通知
    @AfterThrowing(pointcut = "serviceLayerExecution()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {
        System.out.println("Executing @AfterThrowing advice on method: " + joinPoint.getSignature() + ", exception: " + exception.getMessage());
    }
}

在这个示例中,我们定义了一个名为 LoggingAspect 的切面类,它包含了四种不同的通知方法,分别对应四种不同的通知类型:@Before@After@AfterReturning@AfterThrowing。这些方法会在切入点表达式 serviceLayerExecution() 指定的方法调用前后执行相应的逻辑。

注意事项

  • 确保你的 Spring Boot 应用已启用 AOP 支持,通常情况下,如果切面类上有 @Aspect@Component 注解,那么 Spring Boot 会自动识别并配置 AOP 支持。
  • 为了确保切面能够正常工作,确保切入点表达式正确无误,并且覆盖了你希望拦截的方法。
  • 考虑到性能和可维护性,尽量减少切面的复杂度,并保持切面逻辑的单一职责原则。

切面语法

在 Spring AOP 中,execution 是一个切入点表达式,用于指定哪些连接点(即方法调用)应该被切面所关注。execution 表达式允许你根据方法签名来匹配方法调用,包括返回类型、包、类、方法名以及参数列表。

execution 表达式的语法格式如下:

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

其中:

  • modifiers-pattern?:可选的修饰符模式。
  • ret-type-pattern:返回类型模式。
  • declaring-type-pattern?:可选的声明类型模式。
  • name-pattern:方法名模式。
  • param-pattern:参数模式。
  • throws-pattern?:可选的异常模式。

下面是一些具体的例子:

示例 1: 匹配特定的方法

execution(public int com.example.demo.service.MyService.myMethod(int))

这将匹配 com.example.demo.service.MyService 类中的 myMethod 方法,该方法有一个 int 类型的参数并且返回 int 类型的结果。

示例 2: 匹配所有公共方法

execution(public * *(..))

这将匹配所有公共方法,无论其返回类型或参数列表是什么。

示例 3: 匹配所有方法

execution(* *.*(..))

这将匹配所有方法,无论其返回类型、方法名称、所在类或参数列表。

示例 4: 匹配特定包下的所有方法

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

这将匹配 com.example.demo.service 包下所有类的所有方法。

示例 5: 匹配具有特定参数的方法

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

这将匹配 com.example.demo.service 包下所有类的所有方法,只要其中一个参数是 String 类型即可。

示例 6: 匹配特定返回类型的方法

execution(String *.*(..))

这将匹配所有返回类型为 String 的方法。

示例 7: 匹配没有参数的方法

execution(* *.*() throws ..)

这将匹配所有没有参数的方法,并且允许方法抛出异常。

示例 8: 匹配特定异常的方法

execution(* *.*(..) throws java.io.IOException)

这将匹配所有可能抛出 java.io.IOException 异常的方法。

示例 9: 匹配特定方法签名

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

这将匹配 com.example.demo.service 包下所有类的任何以 get 开头的方法。

使用这些表达式可以帮助你精确地控制哪些方法会被切面所影响。在编写切入点表达式时,请确保它们足够具体,以避免意外地匹配到其他你不希望影响的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

问道飞鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值