面试 — 进行时 — Spring AOP

好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受.

说说 spring aop 的那些事。

Spring AOP 的前世今生

  • 起源与发展 :AOP(Aspect-Oriented Programming,面向切面编程)作为一种编程思想,旨在解决传统面向对象编程中存在的代码重复、关注点分散等问题。Spring AOP 是 Spring 框架对 AOP 思想的实现,它借鉴了 AOP Alliance 的相关规范,并不断发展和完善。

  • 与 AspectJ 的关系 :早期 Spring AOP 和 AspectJ 是相互竞争的 AOP 框架,但后来 Spring 决定整合 AspectJ,从 Spring 2.0 开始对 AspectJ 提供支持,并在后续版本中不断加深融合。不过,Spring AOP 和 AspectJ 在实现原理和使用场景上有所不同,Spring AOP 主要基于动态代理实现运行时增强,而 AspectJ 是基于字节码操作的编译时增强。

Spring AOP 的核心设计思想

  • 代码复用 :将通用的功能(如日志记录、事务管理等)抽取出来,形成可复用的切面模块,避免在多个业务模块中重复编写相同的代码,提高代码的可维护性和可扩展性。

  • 关注点分离 :将业务逻辑和通用功能分离,让开发者能够更专注于业务逻辑的实现,而通用功能则由切面来处理,从而使代码更加清晰、简洁,降低模块间的耦合度。

  • 动态织入 :在运行时通过动态代理技术将切面逻辑织入到目标对象的方法中,无需修改目标对象的源代码,实现了对目标对象的无侵入式增强,增强了系统的灵活性和可扩展性。

  • 基于接口与注解的编程模式 :提供了丰富的注解(如 @Aspect、@Before、@After、@Around 等),方便开发者快速定义切面和切点,降低了 AOP 编程的门槛,同时也支持基于接口的编程模式,与 Spring 容器紧密结合,方便对切面进行管理。

  • 与 Spring 容器的集成 :依赖于 Spring 的 IOC 容器,能够方便地获取和管理切面 Bean,同时也能够利用 Spring 容器的其他功能(如依赖注入等)来增强切面的灵活性和可配置性。

Spring AOP 的核心实现源码

  • AOP 代理的创建 :Spring AOP 通过 AopProxyFactory 来创建 AOP 代理对象,它会根据目标对象是否实现了接口以及相关配置来决定使用 JDK 动态代理还是 CGLIB 代理。其核心实现代码如下:扫码添加如下号码,了解更多信息。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }
}
  • 方法拦截器链的执行 :在 Spring AOP 中,方法拦截器链的执行是通过 ReflectiveMethodInvocation 类来实现的,其核心代码如下:

public class ReflectiveMethodInvocation extends AbstractMethodInvocation {
    // 方法拦截器链
    private final List<Object> interceptorsAndDynamicMethodMatchers;
    private final Class<?> targetClass;
    private final Object[] arguments;
    private final Method method;
    private final Object target;
    // 其他代码...

    public Object proceed() throws Throwable {
        Object returnType = null;
        for (int i = 0; i < interceptorsAndDynamicMethodMatchers.size() - 1; i++) {
            Object interceptorOrInterceptionAdvice = interceptorsAndDynamicMethodMatchers.get(i);
            if (interceptorOrInterceptionAdvice instanceof MethodInterceptor) {
                returnType = ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
                if (returnType != null) {
                    return returnType;
                }
            }
        }
        return invokeJoinpoint();
    }

    private Object invokeJoinpoint() throws Throwable {
        return AopUtils.invokeJoinpointUsingReflection(target, method, arguments);
    }
}
  • 环绕通知的实现 :环绕通知是 Spring AOP 中一种强大的通知类型,它允许在目标方法执行前后都执行自定义逻辑,并且可以控制目标方法是否执行以及返回值等。环绕通知的核心实现代码如下:

public class AroundAdviceInterceptor implements MethodInterceptor {
    private final MethodInterceptor interceptor;

    public AroundAdviceInterceptor(MethodInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return this.interceptor.invoke(invocation);
    }
}

Spring AOP 的使用场景

  • 日志记录 :在方法执行前后记录日志信息,方便对系统的运行情况进行监控和分析。

  • 权限验证 :在业务方法执行前进行权限验证,确保只有具有相应权限的用户才能访问特定的方法或功能。

  • 性能监控 :通过环绕通知测量方法的执行时间,对系统的性能进行监控和优化。

  • 事务管理 :对数据访问层的方法进行事务管理,确保数据的一致性和完整性。

  • 缓存管理 :在方法执行前检查缓存中是否存在结果,如果存在则直接返回缓存结果,否则执行方法并将结果存入缓存。

  • 异常处理 :在方法执行出现异常时进行统一的异常处理,如记录异常信息、发送警报邮件等。

Spring AOP 的替代方案

  • AspectJ :AspectJ 是一个功能强大的 AOP 框架,它基于字节码操作实现编译时增强,能够对类的字段、构造方法等进行增强,且性能较好。Spring AOP 已经集成了 AspectJ,但在使用时需要注意二者的区别和适用场景。

  • 字节码增强工具 :如 ASM、Javassist 等,它们可以直接操作字节码来实现对类的增强,具有较高的灵活性和性能,但使用相对复杂,需要对字节码有一定的了解。

  • 其他 AOP 框架 :如 Guice AOP 等,它们也提供了类似的功能,可根据具体需求选择合适的框架。

### Spring AOP 面试题整理 Spring AOP(Aspect-Oriented Programming)是 Spring 框架中的一个重要模块,用于实现面向切面编程。以下是关于 Spring AOP 的常见面试题和知识点总结: #### 1. Spring AOP 的核心概念是什么? Spring AOP 的核心概念包括以下几个方面: - **Aspect**:切面,封装了横切逻辑,通常是一个包含 @Aspect 注解的类[^2]。 - **Join Point**:连接点,程序执行过程中的某个阶段,例如方法调用或异常抛出。 - **Advice**:通知,定义了在特定 Join Point 上执行的动作,包括 Before、After、Around 等类型[^2]。 - **Pointcut**:切入点,定义了 Advice 应该在哪些 Join Point 上生效[^2]。 - **Target Object**:目标对象,被代理的对象,即需要增强的业务对象[^2]。 - **AOP Proxy**:AOP 代理,由 Spring 动态生成的对象,用于实现 AOP 功能。 #### 2. Spring AOP 和 AspectJ 的区别是什么? Spring AOP 是基于动态代理实现的运行时增强,而 AspectJ 是编译时增强。主要区别如下: - **实现方式**:Spring AOP 基于动态代理(JDK 或 CGLIB),AspectJ 使用字节码操作技术[^2]。 - **性能**:Spring AOP 在容器启动时生成代理实例,性能略逊于 AspectJ。 - **功能范围**:Spring AOP 只能作用于 Spring 容器管理的对象,而 AspectJ 可以作用于任何 Java 对象[^2]。 #### 3. Spring AOP 的代理机制有哪些? Spring AOP 支持两种代理机制: - **JDK 动态代理**:当目标对象实现了接口时,默认使用 JDK 动态代理[^4]。 - **CGLIB 代理**:当目标对象没有实现接口时,默认使用 CGLIB 代理[^4]。 #### 4. Spring AOP 的通知类型有哪些? Spring AOP 提供了以下几种通知类型: - **Before Advice**:在方法执行之前执行的通知。 - **After Returning Advice**:在方法成功返回后执行的通知[^2]。 - **After Throwing Advice**:在方法抛出异常后执行的通知[^2]。 - **After (Finally) Advice**:无论方法是否抛出异常都会执行的通知。 - **Around Advice**:环绕通知,在方法执行前后都可以执行自定义逻辑[^2]。 #### 5. 如何配置 Spring AOPSpring AOP 的配置可以通过 XML 或注解实现: - **XML 配置**:通过 `<aop:config>` 标签定义切面和切入点。 - **注解配置**:使用 `@Aspect` 定义切面,`@Before`、`@After` 等定义通知。 #### 6. Spring AOP 的默认行为是什么? Spring AOP 默认使用 JDK 动态代理。如果目标对象没有实现接口,则会自动切换到 CGLIB 代理[^4]。 #### 7. Spring AOP 的局限性有哪些? Spring AOP 存在以下局限性: - 只能对 Spring 容器管理的对象进行增强[^2]。 - 无法增强私有方法、构造函数或 final 方法[^2]。 - 性能开销较大,尤其是使用 CGLIB 代理时[^2]。 #### 8. Spring AOP 的源码分析 Spring AOP 的核心类包括: - **ProxyFactoryBean**:用于创建 AOP 代理对象[^1]。 - **Advisor** 和 **Pointcut**:分别定义通知和切入点。 - **MethodInterceptor**:用于拦截方法调用并执行自定义逻辑。 以下是简单的 AOP 示例代码: ```java @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("Executing method: " + joinPoint.getSignature().getName()); } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值