Skip to content

AOP原理分析

在前面的几篇文章中,我们介绍了Spring AOP的基本概念、配置方式和高级特性。本文将深入探讨Spring AOP的实现原理,帮助大家理解Spring AOP是如何工作的,以及在使用过程中可能遇到的一些限制。

1. Spring AOP的代理机制

Spring AOP是基于代理(Proxy)实现的。代理是一种设计模式,它允许在不改变原始对象代码的情况下,通过引入一个代理对象来控制对原始对象的访问,从而实现在方法调用前后添加额外行为的目的。

Spring AOP支持两种代理机制:

1.1 JDK动态代理

JDK动态代理是Java标准库提供的代理机制,它要求代理的类必须实现至少一个接口。

工作原理

  1. 创建一个实现InvocationHandler接口的处理器
  2. 使用Proxy.newProxyInstance()方法创建代理实例
  3. 当调用代理对象的方法时,调用会被转发到InvocationHandler.invoke()方法

简化的JDK动态代理示例

java
// 定义接口
interface UserService {
    void save(User user);
}

// 实现类
class UserServiceImpl implements UserService {
    public void save(User user) {
        System.out.println("保存用户: " + user.getName());
    }
}

// 调用处理器
class AopInvocationHandler implements InvocationHandler {
    private final Object target;
    
    public AopInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置通知: 方法执行前");
        
        // 调用目标方法
        Object result = method.invoke(target, args);
        
        System.out.println("后置通知: 方法执行后");
        return result;
    }
}

// 创建代理
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class[] { UserService.class },
    new AopInvocationHandler(userService)
);

// 调用代理方法
proxy.save(new User("张三"));

1.2 CGLIB代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,Spring AOP使用它为没有实现接口的类创建代理。

工作原理

  1. CGLIB通过生成目标类的子类来创建代理
  2. 在子类中重写父类的方法,添加增强逻辑
  3. 使用CGLIB的Enhancer类来创建代理对象

简化的CGLIB代理示例

java
// 目标类 (没有实现任何接口)
class UserService {
    public void save(User user) {
        System.out.println("保存用户: " + user.getName());
    }
}

// 方法拦截器
class AopMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置通知: 方法执行前");
        
        // 调用原始方法
        Object result = proxy.invokeSuper(obj, args);
        
        System.out.println("后置通知: 方法执行后");
        return result;
    }
}

// 创建代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new AopMethodInterceptor());
UserService proxy = (UserService) enhancer.create();

// 调用代理方法
proxy.save(new User("张三"));

1.3 Spring如何选择代理方式

Spring在创建AOP代理时,会按照以下规则选择代理方式:

  1. 如果目标类实现了至少一个接口,默认使用JDK动态代理
  2. 如果目标类没有实现任何接口,使用CGLIB代理
  3. 如果强制指定使用CGLIB(通过配置proxyTargetClass=true),则始终使用CGLIB代理
java
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB代理
@Configuration
public class AppConfig {
    // ...
}

2. Spring AOP的初始化流程

Spring AOP的实现是通过一系列的初始化步骤来完成的,下面我们来详细分析这个流程。

2.1 AOP配置解析

当Spring容器启动时,它会首先解析配置(XML或注解)并注册所有的bean定义。对于AOP配置:

  1. 基于XML的配置:Spring解析<aop:config><aop:aspect>等元素,创建对应的advisor和advice
  2. 基于注解的配置:Spring识别@Aspect注解的类,并解析其中的@Pointcut@Before等注解

在解析过程中,Spring会将切面、切点和通知信息转换为内部表示,主要是创建各种AdvisorAdvicePointcut对象。

2.2 代理创建过程

Spring在创建bean实例时,会执行以下步骤来创建AOP代理:

  1. 实例化原始bean:首先,Spring会像普通bean一样实例化目标对象
  2. 检查是否需要代理:Spring检查该bean是否匹配任何切点表达式
  3. 创建AOP代理:如果需要代理,Spring会使用ProxyFactory创建代理对象

代理创建的关键类和接口包括:

  • AbstractAutoProxyCreator:自动代理创建器的抽象基类
  • DefaultAdvisorAutoProxyCreator:基于Advisor的自动代理创建器
  • AnnotationAwareAspectJAutoProxyCreator:处理@AspectJ注解的代理创建器
  • ProxyFactory:创建AOP代理的工厂类
  • AopProxy:代理生成的策略接口
    • JdkDynamicAopProxy:使用JDK动态代理的实现
    • CglibAopProxy:使用CGLIB的实现

2.3 初始化流程图

Spring AOP初始化流程可以简化为以下步骤:

加载Spring配置

解析AOP配置(XML或注解)

注册Bean后处理器(如AnnotationAwareAspectJAutoProxyCreator)

实例化Bean

Bean后处理阶段 → AbstractAutoProxyCreator.postProcessAfterInitialization()
   |                    ↓
   |           找到与Bean匹配的Advisor
   |                    ↓
   |         创建ProxyFactory并设置Advisor
   |                    ↓
   |          根据条件选择代理方式(JDK或CGLIB)
   |                    ↓
   |             创建并返回代理对象

返回代理对象(替代原始Bean)

3. AOP代理的调用链路

当我们调用一个被Spring AOP代理的方法时,调用会经过一系列处理步骤,下面我们来看看这个调用链路。

3.1 JDK动态代理调用链路

调用代理对象方法

JdkDynamicAopProxy.invoke()

获取匹配当前方法的拦截器链

创建ReflectiveMethodInvocation

调用ReflectiveMethodInvocation.proceed()

依次执行拦截器链中的拦截器
   |
   ├→ ExposeInvocationInterceptor
   ├→ 前置通知拦截器
   ├→ 环绕通知拦截器开始部分
   |

反射调用目标方法
   |

   |
   ├→ 环绕通知拦截器结束部分
   ├→ 返回通知拦截器
   ├→ 异常通知拦截器(如果有异常)
   ├→ 后置通知拦截器
   |

返回方法结果或抛出异常

3.2 CGLIB代理调用链路

CGLIB代理的调用链路与JDK动态代理类似,主要区别在于入口是DynamicAdvisedInterceptor.intercept()方法:

调用代理对象方法

CglibAopProxy.DynamicAdvisedInterceptor.intercept()

获取匹配当前方法的拦截器链

创建CglibMethodInvocation

调用CglibMethodInvocation.proceed()

依次执行拦截器链中的拦截器
   |
   ├→ ExposeInvocationInterceptor
   ├→ 前置通知拦截器
   ├→ 环绕通知拦截器开始部分
   |

调用目标方法
   |

   |
   ├→ 环绕通知拦截器结束部分
   ├→ 返回通知拦截器
   ├→ 异常通知拦截器(如果有异常)
   ├→ 后置通知拦截器
   |

返回方法结果或抛出异常

3.3 拦截器链的构建

拦截器链是AOP调用过程中的核心概念,它包含了所有与当前方法匹配的通知,按照特定顺序排列:

  1. 获取所有适用的Advisor:根据方法和目标类找到所有匹配的Advisor
  2. 转换为MethodInterceptor:将Advisor中的Advice转换为MethodInterceptor
  3. 排序:按照优先级对拦截器进行排序
  4. 执行:按照特定顺序执行拦截器链

拦截器链的排序遵循以下原则:

  • 前置通知:优先级高的先执行
  • 后置、返回、异常通知:优先级低的先执行
  • 环绕通知:遵循与前置和后置通知相同的规则

4. 切点表达式的解析与匹配

Spring AOP中的切点表达式决定了哪些方法会被代理,下面我们来看看切点表达式是如何解析和匹配的。

4.1 切点表达式解析过程

  1. 字符串形式的切点表达式被传递给PointcutParser
  2. 解析器将表达式解析为语法树
  3. 生成适当的Pointcut对象
    • 对于AspectJ表达式,生成AspectJExpressionPointcut
    • 对于自定义表达式,使用相应的Pointcut实现

4.2 匹配过程

切点匹配分为两个阶段:

  1. 静态匹配:根据方法签名和类信息进行匹配,主要检查:

    • 类是否匹配(使用ClassFilter
    • 方法签名是否匹配(使用MethodMatcher.matches(Method, Class)
  2. 运行时匹配(可选):如果MethodMatcher.isRuntime()返回true,则进行运行时匹配,检查:

    • 方法参数是否匹配(使用MethodMatcher.matches(Method, Class, Object[])

4.3 常见切点表达式的解析

以下是一些常见切点表达式的解析示例:

  1. execution

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

    解析为:

    • ClassFilter: 检查类是否属于com.example.service
    • MethodMatcher: 检查方法是否是public,以及方法名和参数
  2. @annotation

    @annotation(org.springframework.transaction.annotation.Transactional)

    解析为:

    • ClassFilter: 返回true(不限制类)
    • MethodMatcher: 检查方法是否带有@Transactional注解
  3. within

    within(com.example.service.*)

    解析为:

    • ClassFilter: 检查类是否属于com.example.service包直接子类
    • MethodMatcher: 返回true(不限制方法)

5. Spring AOP的内部核心类

了解Spring AOP的核心类有助于深入理解其工作原理。以下是一些重要的核心类:

5.1 代理创建相关

  • ProxyConfig: 代理配置的基类
  • AdvisedSupport: 代理配置的核心类,包含Advice和Advisor列表
  • ProxyFactory: 创建AOP代理的工厂
  • ProxyFactoryBean: 用于在XML配置中创建代理的FactoryBean
  • AbstractAutoProxyCreator: 自动创建代理的BeanPostProcessor

5.2 切点和通知相关

  • Pointcut: 切点接口,定义如何选择连接点
    • ClassFilter: 过滤类
    • MethodMatcher: 匹配方法
  • Advice: 通知接口,定义在连接点处要执行的行为
    • BeforeAdvice: 前置通知
    • AfterReturningAdvice: 返回通知
    • ThrowsAdvice: 异常通知
    • MethodInterceptor: 环绕通知
  • Advisor: 将Pointcut和Advice组合起来的接口
    • PointcutAdvisor: 基于切点的Advisor

5.3 AOP调用相关

  • AopProxy: 代理创建策略接口
    • JdkDynamicAopProxy: JDK动态代理实现
    • CglibAopProxy: CGLIB代理实现
  • MethodInvocation: 方法调用的表示
    • ReflectiveMethodInvocation: 反射方法调用
    • CglibMethodInvocation: CGLIB方法调用
  • AopContext: 提供对当前代理对象的访问

5.4 AspectJ集成相关

  • AspectJExpressionPointcut: 使用AspectJ表达式的切点
  • AspectJPointcutAdvisor: 使用AspectJ表达式的Advisor
  • AspectJAdvisorFactory: 创建基于@AspectJ注解的Advisor
  • ReflectiveAspectJAdvisorFactory: AspectJAdvisorFactory的反射实现

6. Spring AOP的限制与解决方案

Spring AOP虽然强大,但它也有一些固有的限制,了解这些限制和可能的解决方案对于正确使用AOP至关重要。

6.1 自调用问题(Self-invocation)

限制:当一个bean内部的方法直接调用同一个bean的另一个方法时,AOP通知不会被触发。这是因为内部调用不经过代理对象。

java
@Service
public class UserService {
    @Transactional // 不会生效
    public void updateUser(User user) {
        // 业务逻辑
    }
    
    public void process() {
        User user = new User();
        // 直接调用,不会触发事务
        this.updateUser(user);
    }
}

解决方案

  1. 自注入代理

    java
    @Service
    public class UserService {
        @Autowired
        @Lazy // 避免循环依赖
        private UserService self;
        
        @Transactional
        public void updateUser(User user) {
            // 业务逻辑
        }
        
        public void process() {
            User user = new User();
            // 通过代理调用,会触发事务
            self.updateUser(user);
        }
    }
  2. 使用AopContext(需要启用exposeProxy):

    java
    @EnableAspectJAutoProxy(exposeProxy = true)
    @Configuration
    public class AppConfig {
        // ...
    }
    
    @Service
    public class UserService {
        @Transactional
        public void updateUser(User user) {
            // 业务逻辑
        }
        
        public void process() {
            User user = new User();
            // 通过AopContext获取代理
            ((UserService) AopContext.currentProxy()).updateUser(user);
        }
    }
  3. 拆分为两个类:将相关功能拆分到不同的类中,通过注入调用

  4. 使用AspectJ代替Spring AOP:AspectJ不存在自调用问题

6.2 final方法和类

限制:Spring AOP不能代理final方法或类,因为:

  • JDK动态代理基于接口,不受影响
  • CGLIB通过创建子类实现代理,不能继承final类或重写final方法

解决方案

  1. 避免使用final关键字
  2. 确保final方法不需要AOP功能
  3. 使用AspectJ代替Spring AOP

6.3 非public方法

限制:默认情况下,Spring AOP只代理public方法:

  • JDK动态代理只代理接口方法(通常是public)
  • CGLIB可以代理protected和默认访问级别的方法,但需要特殊配置

解决方案

  1. 将方法修改为public

  2. 配置CGLIB代理protected和默认访问级别方法:

    java
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    @Configuration
    public class AppConfig {
        // ...
    }
  3. 使用AspectJ代替Spring AOP

6.4 代理嵌套导致的性能问题

限制:多个切面应用到同一个bean时,可能会创建代理的嵌套链,影响性能。

解决方案

  1. Spring自动优化:Spring会尝试将多个切面合并到一个代理中
  2. 减少切面数量:合并功能相似的切面
  3. 使用AspectJ编译时织入:避免运行时代理开销

6.5 内部不可见的类型

限制:AOP代理把参数和返回值暴露为接口类型而非实现类型,可能导致类型转换问题。

解决方案

  1. 编程时使用接口类型,避免强制类型转换
  2. 使用引入(Introduction)添加额外的接口
  3. 配置proxyTargetClass=true使用CGLIB代理

7. Spring AOP与AspectJ的内部原理对比

Spring AOP和AspectJ都是实现AOP的工具,但它们的内部实现原理有很大区别。

7.1 织入时机

  • Spring AOP:运行时织入,在程序运行时动态创建代理对象
  • AspectJ:支持多种织入时机
    • 编译时织入(Compile-Time Weaving):在编译源代码时织入
    • 编译后织入(Post-Compile Weaving):在编译后但运行前织入
    • 加载时织入(Load-Time Weaving):在类加载时织入

7.2 实现技术

  • Spring AOP:基于代理的AOP实现
    • 使用JDK动态代理或CGLIB创建代理
    • 不修改原始类的字节码
  • AspectJ:基于字节码修改的AOP实现
    • 直接修改类的字节码
    • 使用特殊的编译器或类加载时织入代理

7.3 功能对比

特性Spring AOPAspectJ
连接点类型仅方法执行方法调用、字段访问、构造器、异常处理等
切点表达式支持AspectJ表达式子集完整的AspectJ表达式
性能较低(运行时代理)较高(编译时织入几乎没有运行时开销)
易用性简单(集成在Spring中)复杂(需要额外的编译器或LTW配置)
字节码修改不修改直接修改
织入非Spring Bean不支持支持
自调用问题存在不存在

7.4 适用场景

  • Spring AOP适合

    • Spring项目中的简单横切关注点
    • 方法级别的AOP需求
    • 不需要修改框架或第三方库代码
  • AspectJ适合

    • 需要非方法连接点(如字段访问)
    • 需要拦截非public方法或final类
    • 需要拦截自调用
    • 对性能要求极高的场景
    • 需要拦截未由Spring管理的对象

8. 源码跟踪:AOP代理创建

为了更深入地理解Spring AOP的实现原理,我们来跟踪一下创建AOP代理的关键源码流程。

8.1 代理创建的入口

Spring AOP代理创建的主要入口是AbstractAutoProxyCreator.postProcessAfterInitialization()方法:

java
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 如果需要,创建代理
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

8.2 判断是否需要创建代理

wrapIfNecessary方法中,Spring会检查是否需要为bean创建代理:

java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 跳过已处理的bean
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    // 跳过不需要代理的bean(如AOP基础设施类)
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 查找适用于此bean的通知器
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

8.3 查找适用的通知器

AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean()方法负责查找适用于特定bean的通知器:

java
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 查找所有Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 筛选适用于此bean的Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 对Advisor进行排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

8.4 创建代理

AbstractAutoProxyCreator.createProxy()方法负责创建代理对象:

java
protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    
    // 创建ProxyFactory
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    // 决定是否使用CGLIB
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            // 检查是否有接口
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 构建Advisor链
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);
    }

    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    // 控制是否冻结代理,冻结后无法添加新的通知
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 获取代理
    return proxyFactory.getProxy(getProxyClassLoader());
}

8.5 选择代理方式并创建代理

DefaultAopProxyFactory.createAopProxy()方法负责选择使用JDK动态代理还是CGLIB:

java
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 判断是否使用CGLIB
    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.");
        }
        // 如果目标类是接口或已经是代理,使用JDK动态代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 否则使用CGLIB
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // 默认使用JDK动态代理
        return new JdkDynamicAopProxy(config);
    }
}

9. 源码分析:AOP代理方法调用

当我们调用一个被Spring AOP代理的方法时,调用会被拦截并执行通知链。下面我们来看一下这个过程的源码实现。

9.1 JDK动态代理调用

当使用JDK动态代理时,所有方法调用都会被路由到JdkDynamicAopProxy.invoke()方法:

java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.getTargetSource();
    Class<?> targetClass = null;
    Object target = null;

    try {
        // 跳过Object类的方法,如equals()、hashCode()等
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 实现代理对象的equals方法
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 实现代理对象的hashCode方法
            return hashCode();
        }
        // ...其他特殊方法处理...

        // 设置暴露代理
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // 获取目标对象
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }

        // 获取当前方法的拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        Object retVal;
        
        // 如果没有拦截器,直接调用目标方法
        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 创建方法调用对象
            MethodInvocation invocation = new ReflectiveMethodInvocation(
                    proxy, target, method, args, targetClass, chain);
            // 执行拦截器链
            retVal = invocation.proceed();
        }

        // 处理返回值类型
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target && returnType != Object.class
                && returnType.isInstance(proxy)) {
            retVal = proxy;
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

9.2 CGLIB代理调用

CGLIB代理通过DynamicAdvisedInterceptor.intercept()方法拦截方法调用:

java
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Object target = null;
    TargetSource targetSource = this.advised.getTargetSource();
    try {
        // 与JDK代理类似的逻辑...
        
        // 获取拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;

        // 如果没有拦截器,直接调用目标方法
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
            // 创建方法调用对象
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        
        // 处理返回值...
        return retVal;
    }
    finally {
        // 清理资源...
    }
}

9.3 拦截器链的执行

拦截器链的执行是通过ReflectiveMethodInvocation.proceed()方法完成的:

java
@Override
public Object proceed() throws Throwable {
    // 如果已经执行完所有拦截器,调用目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // 获取下一个拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    
    // 处理动态拦截器
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 跳过这个拦截器,继续下一个
            return proceed();
        }
    }
    else {
        // 调用普通拦截器
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

9.4 常见通知类型的拦截器实现

每种通知类型都会被转换为相应的拦截器:

  1. 前置通知拦截器MethodBeforeAdviceInterceptor

    java
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        // 在目标方法执行前执行通知
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        // 继续执行拦截器链
        return mi.proceed();
    }
  2. 后置通知拦截器AfterReturningAdviceInterceptor

    java
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        // 在目标方法正常返回后执行通知
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }
  3. 异常通知拦截器ThrowsAdviceInterceptor

    java
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            return mi.proceed();
        }
        catch (Throwable ex) {
            // 查找匹配异常类型的通知方法
            Method handlerMethod = getExceptionHandler(ex);
            if (handlerMethod != null) {
                // 调用异常通知
                invokeHandlerMethod(mi, ex, handlerMethod);
            }
            throw ex;
        }
    }
  4. 环绕通知拦截器:直接使用用户提供的MethodInterceptor

    java
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        // 用户实现的环绕通知逻辑,可以控制是否调用mi.proceed()
        // 例如:
        try {
            // 前置逻辑
            Object result = mi.proceed();
            // 后置逻辑
            return result;
        } catch (Exception ex) {
            // 异常处理逻辑
            throw ex;
        }
    }

10. 最佳实践总结

基于对Spring AOP内部原理的深入理解,我们可以总结出以下最佳实践建议:

10.1 性能考虑

  1. 避免过度使用AOP:AOP虽然强大,但会带来额外的运行时开销
  2. 合理使用切点表达式:过于宽泛的切点表达式可能导致不必要的代理
  3. 对性能关键的应用考虑使用AspectJ:编译时织入几乎没有运行时开销
  4. 减少通知的嵌套层次:每一层通知都会增加方法调用的开销

10.2 代理选择

  1. 基于接口的设计:遵循面向接口编程的原则,使JDK动态代理更有效
  2. 明确选择代理方式:根据需要显式设置proxyTargetClass属性
  3. 注意CGLIB的限制:使用CGLIB时注意final方法和类的限制

10.3 切面设计

  1. 保持切面的单一职责:一个切面应该只关注一个横切关注点
  2. 正确使用通知类型:根据需求选择合适的通知类型
  3. 合理设置切面优先级:使用@OrderOrdered接口控制切面执行顺序
  4. 避免在通知中执行耗时操作:特别是在前置通知和环绕通知中

10.4 自调用处理

  1. 意识到自调用问题:了解内部方法调用不会触发AOP通知
  2. 使用推荐的解决方案:自注入代理、使用AopContext或拆分类

10.5 异常处理

  1. 合理使用异常通知:在异常通知中不要捕获您不能处理的异常
  2. 注意异常转换:环绕通知可能会改变原始异常,影响异常通知
  3. 合理处理通知中的异常:通知本身抛出的异常会覆盖原始方法的异常

10.6 测试

  1. 测试代理行为:验证AOP代理如预期工作
  2. 模拟AOP环境:在单元测试中模拟AOP代理
  3. 测试边界情况:如内部调用、异常处理等

总结

本文深入探讨了Spring AOP的内部实现原理,从代理创建机制、切点表达式解析、拦截器链执行到源码分析,全面解析了Spring AOP的工作原理。

理解Spring AOP的内部原理不仅有助于我们更有效地使用AOP,还能帮助我们在遇到问题时更快地找到解决方案。虽然Spring AOP有一些固有的限制,但通过合理的设计和实践,我们可以充分发挥AOP的强大功能,构建更加模块化、可维护的应用系统。