SpringAOP

谈一谈

AOP(面向切面编程,Aspect-Oriented Programming)能够将那些与业务无关,却为业务模块所共同调用的逻辑和责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度并有利于未来的可扩展性和可维护性

Spring AOP是基于 动态代理:

  • 代理对象实现了某个接口:AOP就会使用 JDK动态代理对象
  • 代理对象没有实现接口:AOP就会使用 CGlib动态代理 生成一个被代理对象的子类来作为代理。

我们也可以使用AspectJ,Spring AOP中集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。

AOP使用的话: 我们可以把一些通知功能抽象出来,在需要用到的地方用AOP切入即可,这样可大大简化代码量。我们需要增加新功能也方便。提高了系统的可扩展性。日志功能、事务管理和权限管理等场景都用到AOP。

Spring的使用

首先,创建一个目标对象,该对象包含一个需要增强的方法:

arduino 复制代码
public class CalculatorService {
    public int add(int a, int b) {
        return a + b;
    }
}

然后,定义一个切面来处理日志记录:

java 复制代码
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.service.*.*(..))")
    public void loggingPointcut() {
    }

    @Before("loggingPointcut()")
    public void logBefore() {
        System.out.println("Before method call");
    }
}

在Spring配置文件中,启用Spring AOP:

makefile 复制代码
<aop:aspectj-autoproxy />

或者,如果你使用Java配置:

less 复制代码
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    // Other configurations
}

现在,当调用目标对象的方法时,Spring AOP会自动应用切面并执行通知:

arduino 复制代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
        CalculatorService calculatorService = context.getBean(CalculatorService.class);

        int result = calculatorService.add(1, 2);
        System.out.println("Result: " + result);
    }
}

在这个例子中,当CalculatorServiceadd方法被调用时,LoggingAspectlogBefore方法也会被调用,从而实现了日志记录的功能。

AOP核心概念

  • 连接点(Join Point)

    • 连接点是应用程序执行流程中的一个点,例如方法的执行、异常抛出、类初始化等。在Spring AOP中,最常见的连接点是 方法的执行
  • 切点(Pointcut)

    • 切点是对连接点的逻辑组合,它定义了 哪些连接点会被特定的Aspect所拦截。切点通常基于方法签名、异常类型、注解等来定义。
  • 通知(Advice)

    • 通知是AOP框架在特定连接点执行的动作。通知可以分为前置通知、后置通知、环绕通知、异常通知和最终通知。通知定义了在连接点执行的动作,如日志记录、事务管理等。
  • 横切关注点

    • 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
  • 切面(Aspect)

    • 切面是关注点的模块化,它将横切关注点与业务逻辑分离。一个切面可以包含一个或多个通知,并且可以将其应用于多个连接点。
  • 目标对象(Target Object)

    • 目标对象是被代理的对象,它包含实际的业务逻辑。AOP代理会拦截目标对象的方法调用,并可能将其转发给相应的切面。
  • 代理(Proxy)

    • 代理是AOP框架创建的对象,它包含了对目标对象的代理。代理拦截对目标对象的调用,并可能将其转发给相应的切面。
  • 织入(Weaving)

    • 在Spring AOP中,织入是 在运行时通过CGLIB或JDK动态代理完成的过程
  • 引入(Introduction)

    • 在不修改代码的情况下向类添加新的方法或字段。 这可以通过在切面中定义新的方法或字段,并在织入时将它们添加到目标对象中来实现。

Spring AOP怎么实现横切关注点的模块化和分离:

  1. ProxyFactoryBean

    • Spring早期版本中使用ProxyFactoryBean来创建代理对象。ProxyFactoryBean可以创建基于JDK动态代理或CGLIB代理的代理。
  2. JDK动态代理

    • 如果目标对象实现了接口,Spring AOP默认使用JDK动态代理。ProxyFactoryBean会自动选择JDK动态代理。
  3. CGLIB代理

    • 如果目标对象没有实现接口,Spring AOP会使用CGLIB来创建代理。ProxyFactoryBean会自动选择CGLIB代理。
  4. AspectJ

    • Spring AOP使用AspectJ来定义切面、切入点、通知等概念。AspectJ提供了丰富的AOP功能,如方法拦截、环绕通知、增强方法等。

Spring AOP的执行流程:

  1. 定义切面

    • 开发者定义一个切面类,其中包含通知方法。切面类可以包含前置通知、后置通知、环绕通知、异常通知和最终通知等。
  2. 定义切入点

    • 开发者定义切入点表达式,指定哪些连接点会被切面拦截。切入点表达式基于方法签名、异常类型、注解等信息来定义。
  3. 配置Spring AOP

    • 在Spring配置文件中,开发者配置Spring AOP,告诉Spring在哪些切点应用切面。
  4. 创建代理

    • Spring AOP会根据目标对象是否实现接口以及是否强制使用CGLIB来决定使用JDK动态代理还是CGLIB。
    • 代理对象会拦截对目标对象的调用,并应用相应的切面。
  5. 调用目标对象

    • 当通过代理对象调用方法时,代理对象会检查是否需要应用切面。如果需要,它将执行相应的通知方法,然后继续执行目标对象的方法。
  6. 执行通知

    • 通知方法在目标对象的方法调用前后执行,允许开发者自定义方法调用的行为,如日志记录、事务管理等。

通过这种方式,Spring AOP实现了横切关注点的模块化和分离,提高了代码的可维护性和可重用性。

面试题精粹:

Spring AOP 和 AspectJ AOP有什么区别

Spring AOP:

  • 运行时增强
  • 基于代理(Proxying)

AspectJ AOP:

  • 编译时增强
  • 基于字节码操作(Bytecode Manipulation)

AspectJ相比于Spring AOP功能更强大,但Spring AOP相对来说更简单。

如果切面比较少,两者差异不大。如果切面比较多,最好选择AspectJ,相对于会快很多。

什么是通知呢?有哪些类型呢?

通知 是这个方法在执行前和执行后要做的动作。就是程序执行时要通过Spring AOP框架触发的代码段。

Spring切面可以应用五种类型的通知:

  • before:前置通知,在一个方法执行前被调用。
  • after:在方法执行之后调用的通知,无论方法执行是否成功。
  • after-returning:仅当方法成功完成后执行的通知。
  • after-throwing:在方法抛出异常退出时执行的通知。
  • around:在方法执行之前和之后调用的通知。

相关推荐
前行的小黑炭7 分钟前
设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。
android·java·kotlin
Java技术小馆12 分钟前
如何设计一个本地缓存
java·面试·架构
XuanXu1 小时前
Java AQS原理以及应用
java
风象南4 小时前
SpringBoot中6种自定义starter开发方法
java·spring boot·后端
mghio13 小时前
Dubbo 中的集群容错
java·微服务·dubbo
咖啡教室18 小时前
java日常开发笔记和开发问题记录
java
咖啡教室18 小时前
java练习项目记录笔记
java
鱼樱前端18 小时前
maven的基础安装和使用--mac/window版本
java·后端
RainbowSea19 小时前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq