📜  讨论Spring AOP(1)

📅  最后修改于: 2023-12-03 14:57:37.280000             🧑  作者: Mango

讨论 Spring AOP

什么是 AOP

AOP(Aspect Oriented Programming)面向切面编程,是一种程序设计思想,它通过预编译方式和运行期动态代理实现程序功能的增强。

在传统的面向对象编程中,一个类包含了所有的功能,各个对象之间耦合性较高。当有多个类需要共同完成一项任务时,就需要在每个类中编写相同的代码,造成代码重复、耦合性高、可维护性差等问题。

AOP 通过在程序运行时动态地将功能插入到指定的地方,实现了代码的解耦和功能的复用。

Spring AOP 的概念

Spring AOP 是 Spring 框架提供的一种切面编程技术。

Spring AOP 提供了一种完全基于 Spring IoC 容器的方式来实现 AOP 功能,它不需要任何特殊的编译器。

Spring AOP 支持声明式的切面编程,允许开发者通过声明式配置来实现对类和方法的拦截,以实现各种功能,例如性能监控、日志统计、事务管理等。

Spring AOP 的特点
  • 基于代理:Spring AOP 使用代理来实现增强,通过代理方式在原有逻辑上增加新的逻辑。
  • 支持 XML 与注解:Spring AOP 可以通过 XML 配置文件或注解的方式来定义切面与增强逻辑。
  • 面向切面:Spring AOP 面向横切面编程,提供了一种能力,允许我们针对模块化横向部分封装。
Spring AOP 的应用场景
  • 日志记录:记录方法的调用,参数和返回值等信息,用于系统日志或调试。
  • 性能统计:统计各类方法的执行时间。
  • 安全检查:在方法执行前或执行后检查用户的权限等信息,以确定用户是否有执行该方法的权限。
  • 事务管理:在方法调用前开启一个事务,在方法调用后提交或者回滚事务。
  • 异常处理:将方法抛出的不同异常转换为统一的异常,或者记录异常信息等。
Spring AOP 的实现方式

Spring AOP 提供了两种方式来实现 AOP:

  • 基于代理的经典 AOP。
  • 基于 AspectJ 的注解驱动的切面。
基于代理的经典 AOP

在 Spring AOP 中,使用代理来实现 AOP。

Spring AOP 只支持方法级别的拦截,主要有以下几种类型的通知:

  • 前置通知(Before):在方法执行前执行;
  • 后置通知(After):在方法执行后执行;
  • 返回通知(AfterReturning):在方法返回结果后执行;
  • 异常通知(AfterThrowing):在方法抛出异常后执行;
  • 环绕通知(Around):在方法执行前后都执行。

代理的生成有两种方式:JDK 动态代理和 CGLIB 字节码生成库。如果目标对象实现了接口,则会使用 JDK 动态代理;否则,会使用 CGLIB 字节码生成库。

基于 AspectJ 的注解驱动的切面

Spring Framework 2.0 开始支持基于 AspectJ 注解驱动的切面。

与基于代理的 AOP 不同,AspectJ 注解驱动的切面是通过在代码中标记切面来实现的。

在使用 AspectJ 注解驱动的切面时,我们需要主要以下两点:

  • 集成 AspectJ 编译器或使用编译时织入;
  • 在配置类中开启 AspectJ 注解驱动的切面,使用 @AspectJ 注解标注切面类,使用 @Pointcut、@Before、@After、@Around 等注解来定义切点以及增强逻辑。
Spring AOP 的使用示例
基于代理的经典 AOP

在 Spring AOP 中,使用代理来实现 AOP。

以下示例演示了如何在切面中记录方法调用时间:

@Aspect
@Component
public class LoggingAspect {

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

    @Around("execution(* com.example.demo..*.*(..))")
    public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long elapsedTime = System.currentTimeMillis() - start;
        LOGGER.info("{}.{}, time elapsed: {}", joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName(), elapsedTime);
        return result;
    }
}

在上面的例子中,使用 @Around 注解定义了一个环绕通知,表示在目标方法执行之前和之后都要执行。

基于 AspectJ 的注解驱动的切面

以下示例演示了如何在切面中记录方法调用时间:

@Aspect
@Component
public class LoggingAspect {

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

    @Pointcut("execution(* com.example.demo..*.*(..))")
    private void logPointcut() {
        // 定义切点
    }

    @Around("logPointcut()")
    public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long elapsedTime = System.currentTimeMillis() - start;
        LOGGER.info("{}.{}, time elapsed: {}", joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName(), elapsedTime);
        return result;
    }
}

在上面的例子中,使用 @Pointcut 注解定义了一个切点,使用 @Around 注解定义了一个环绕通知,表示在目标方法执行之前和之后都要执行。

总结

Spring AOP 是实现 AOP 的一种方式,通过使用动态代理和切面编程技术,可以很好地解决面向对象编程中存在的问题,实现了代码的解耦和功能的复用。

Spring AOP 支持两种实现方式:基于代理的经典 AOP 和基于 AspectJ 的注解驱动的切面,开发者可以根据需求选择适合自己的实现方式。

在实际应用中,Spring AOP 通常用于实现诸如日志记录、性能统计、安全检查、事务管理、异常处理等功能。