Spring AOP 源码解析:注解式切面增强机制

栏目: Java · 发布时间: 5年前

内容简介:IoC 和 AOP 被称为 Spring 两大基础模块,支撑着上层扩展的实现和运行。虽然 AOP 同样建立在 IoC 的实现基础之上,但是作为对 OOP(Object-Oriented Programing) 的补充,AOP(Aspect-Oriented Programming) 在程序设计领域拥有其不可替代的适用场景和地位,而 Spring AOP 作为 AOP 思想的实现,被誉为 spring 框架的基础模块也算是实至名归。Spring 在 1.0 版本的时候就引入了对 AOP 的支持,并且随着版本的

IoC 和 AOP 被称为 Spring 两大基础模块,支撑着上层扩展的实现和运行。虽然 AOP 同样建立在 IoC 的实现基础之上,但是作为对 OOP(Object-Oriented Programing) 的补充,AOP(Aspect-Oriented Programming) 在程序设计领域拥有其不可替代的适用场景和地位,而 Spring AOP 作为 AOP 思想的实现,被誉为 spring 框架的基础模块也算是实至名归。Spring 在 1.0 版本的时候就引入了对 AOP 的支持,并且随着版本的迭代逐渐提供了基于 XML 配置、注解,以及 schema 配置的使用方式,考虑到实际开发中使用注解配置的方式相对较多,所以本文主要分析注解式 AOP 的实现和运行机制。

spring version: 5.1.x

一. 注解式 AOP 示例

首先我们还是通过一个简单的示例来回忆一下注解式 AOP 的具体使用。假设我们声明了一个 IService 接口,并提供了相应的实现类 ServiceImpl,如下:

public interface IService {
    void sayHello();
    void sayHelloTo(String name);
    void sayByebye();
    void sayByebyeTo(String name);
}

@Service
public class ServiceImpl implements IService {

    @Override
    public void sayHello() {
        this.sayHelloTo("zhenchao");
    }

    @Override
    public void sayHelloTo(String name) {
        System.out.println("hello, " + name);
    }

    @Override
    public void sayByebye() {
        this.sayByebyeTo("zhenchao");
    }

    @Override
    public void sayByebyeTo(String name) {
        System.out.println("byebye, " + name);
    }
}

现在我们希望借助 Spring AOP 实现对方法调用的打点功能,首先我们需要定义一个切面:

@Aspect
@Component
public class MetricAspect {

    @Before("execution(* sayHello*(..))")
    public void beforeMetrics4sayHello(JoinPoint point) {
        System.out.println("before metrics for method: " + point.getSignature().getName());
    }

    @After("execution(* sayByebye*(..))")
    public void afterMetrics4sayByebye(JoinPoint point) {
        System.out.println("after metrics for method: " + point.getSignature().getName());
    }

    @Around("execution(* say*(..))")
    public Object aroundMetrics4say(ProceedingJoinPoint point) throws Throwable {
        System.out.println("around:before metrics for method: " + point.getSignature().getName());
        Object obj = point.proceed();
        System.out.println("around:after metrics for method: " + point.getSignature().getName());
        return obj;
    }

}

通过 @Aspect 注解标记 MetricAspect 是一个切面,通过注解 @Before@After ,以及 @Around ,我们在切面中定义了相应的前置、后置,以及环绕增强。然后我们需要在 XML 配置中添加一行如下配置以启用注解式 AOP:

<aop:aspectj-autoproxy/>

然后,我们就算大功告成了。

当然,上面的实现只是注解式 AOP 使用的一个简单示例,并没有覆盖所有的特性,对于 Spring AOP 特性的介绍不属于本文的范畴,不过我们还是会在下面分析源码的过程中进行针对性的介绍。

二. 原理与实现

我们从启用注解式 AOP 的那一行配置切入,即 <aop:aspectj-autoproxy/> 标签。前面我们在分析 IoC 源码时,曾专门分析过 spring 默认标签和自定义标签的解析过程,对于一个标签而言,除了标签的定义,还需要有一个标签的解析器,并在 spring 启动时将标签及其解析器注册到 spring 容器中。标签 <aop:aspectj-autoproxy/> 的注册位于 AopNamespaceHandler#init 方法中:

// 注册 <aspectj-autoproxy/> 标签
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

这里的 AspectJAutoProxyBeanDefinitionParser 类就是标签 <aop:aspectj-autoproxy/> 的解析器,该类实现了 BeanDefinitionParser 接口,并实现了 parse 接口方法,属于标准的标签解析器实现。Spring 容器在启动时会调用 parse 方法实现对标签的解析,方法 AspectJAutoProxyBeanDefinitionParser#parse 的实现如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 注册标签解析器,默认为 AnnotationAwareAspectJAutoProxyCreator
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    // 处理标签的子节点
    this.extendBeanDefinition(element, parserContext);
    return null;
}

该方法做了两件事情:注册标签解析器和处理标签的子节点。这里我们主要来看标签解析器的注册过程,即 AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
    // 1. 注册或更新名为 org.springframework.aop.config.internalAutoProxyCreator 的代理创建器 BeanDefinition
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 2. 处理标签的 proxy-target-class 和 expose-proxy 属性配置
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 3. 注册组件并通知相应的监听器
    registerComponentIfNecessary(beanDefinition, parserContext);
}

我们在代码注释中标明了该方法所做的 3 件事情,其中 1 和 2 是我们分析的关键,首先来看 1 过程所做的事情:

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 如果名为 org.springframework.aop.config.internalAutoProxyCreator 的 bean 已经在册
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            // 已经在册的代理创建器与当前期望注册的不一致,则依据优先级进行选择
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                // 选择优先级高的 bean 进行更新
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    // 没有对应在册的 bean,则注册一个新的
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 注册名为 org.springframework.aop.config.internalAutoProxyCreator 的 bean
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

方法实现虽长,但是逻辑还是挺简单的,即注册一个名为 org.springframework.aop.config.internalAutoProxyCreator 的 bean,我们称之为代理创建器,默认的实现类为 AnnotationAwareAspectJAutoProxyCreator,如果有多个候选实现,则会选择优先级最高的进行注册。

接下来看一下过程 2,该过程主要是用来解析标签 <aop:aspectj-autoproxy/> 的 proxy-target-class 和 expose-proxy 属性配置:

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
    if (sourceElement != null) {
        /*
         * 处理 proxy-target-class 标签:
         * - false 表示使用  java  原生动态代理
         * - true 表示使用 CGLib 动态
         * 但是对于一些没有实现接口的类来说,即使设置为 false 也会使用 CGlib 进行代理
         */
        boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            // 为之前注册的代理创建器 bean 添加一个名为 proxyTargetClass 的属性,值为 true
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }

        /*
         * 处理 expose-proxy 标签:
         * 实现对于内部方法调用的 AOP 增强
         */
        boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            // 为之前注册的代理创建器 bean 添加一个名为 exposeProxy 的属性,值为 true
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

其中 proxy-target-class 属性用来配置是否启用 CGLib 代理,而 expose-proxy 属性则用来配置是否对内部方法调用启用 AOP 增强。属性 proxy-target-class 的作用大家应该都比较属性,下面介绍一下 expose-proxy 属性。前面给出的 AOP 示例中,我们在 sayHello 方法中调用了 sayHelloTo 方法,虽然两个方法都满足对应的 AOP 增强定义,但是只有 sayHello 方法被增强了,这主要是因为 sayHelloTo 方法是在对象内部调用的,调用该方法的对象并不是代理对象。如果期望内部调用也能够被增强,我们需要配置 expose-proxy=true ,并修改 sayHello 对于 sayHelloTo 方法的调用方式:

public void sayHello() {
    ((IService) AopContext.currentProxy()).sayHelloTo("zhenchao");
}

上面分析了这么多,总的说来就是向 spring 容器中注册了一个 AnnotationAwareAspectJAutoProxyCreator 类型的代理创建器 bean,并将配置的 proxy-target-class 和 expose-proxy 属性添加到 bean 的属性列表中。那么 AnnotationAwareAspectJAutoProxyCreator 到底是来做什么的呢?我们先来看一下它的类继承关系图:

Spring AOP 源码解析:注解式切面增强机制

从类继承关系图中我们可以看到该类实现了 BeanPostProcessor 接口,该接口定义如下:

public interface BeanPostProcessor {

    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

由之前对 IoC 容器启动过程的分析我们知道在 IoC 容器启动的过程中,会在初始化 bean 的前后分别调用 BeanPostProcessor 中定义的这两个方法,相应的方法实现主要位于继承链的 AbstractAutoProxyCreator 类中,并且主要是实现了 postProcessAfterInitialization 方法:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 如果 beanName 不为空则直接使用 beanName(FactoryBean 则使用 &{beanName}),否则使用 bean 的 className
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 尝试对 bean 进行增强,创建返回增强后的代理对象
            return this.wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

该方法的核心在于调用 AbstractAutoProxyCreator#wrapIfNecessary 方法尝试基于 AOP 配置对当前 bean 进行增强,并返回增强后的代理对象。方法 wrapIfNecessary 的实现如下:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        // 已经处理过,直接返回
        return bean;
    }

    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        // 不需要进行增强的 bean,直接跳过
        return bean;
    }

    if (this.isInfrastructureClass(bean.getClass()) || this.shouldSkip(bean.getClass(), beanName)) {
        // 对于 AOP 的基础支撑类,或者指定不需要被代理的类,设置为不进行代理
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 获取适用于当前 bean 的增强器
    Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 基于获取到的增强器为当前 bean 创建代理对象
        Object proxy = this.createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

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

方法 wrapIfNecessary 主要的工作是对 bean 进行筛选,筛选掉那些已经增强过的、支持 AOP 基础运行的,以及指定不需要被代理的 bean,对于剩下的 bean 来说,方法首先会调用 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法获取适用于当前 bean 的增强器,并基于这些增强器调用 AbstractAutoProxyCreator#createProxy 方法为当前 bean 创建增强后的代理对象。

2.1 筛选适用于当前 bean 的增强器

我们首先来看一下筛选适用于当前 bean 的增强器的过程:

protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 获取适用于当前 bean 的增强器
    List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        // 没有增强,不进行代理
        return DO_NOT_PROXY; // null
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 获取所有候选的增强器(包括注解的、XML 中定义的)
    List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
    // 从所有增强器中寻找适用于当前 bean 的增强器
    List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 如果增强器不为空,则在最前面追加一个 ExposeInvocationInterceptor
    this.extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 对增强器进行排序
        eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

整个方法的执行流程很简单,获取到所有的候选增强器,并从中找出适用于当前 bean 的增强器。

2.1.1 获取所有的候选增强器

我们先来看一下获取所有候选增强器的过程,位于 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors 方法中:

protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    // 首先调用父类的 findCandidateAdvisors 方法,以提供对于 XML AOP 配置的支持
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    if (this.aspectJAdvisorsBuilder != null) {
        // 获取所有注解定义的增强器
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

方法首先调用了父类的实现,这主要是为了兼容父类查找候选增强器的方式,例如我们的示例中使用的是注解方式定义的增强,但是父类却是基于 XML 配置的方式来查找增强器,这里的兼容能够让我们在以注解方式编程时兼容其它以 XML 配置的方式定义的增强。不过我们还是将主要精力放在解析注解式增强定义上,该过程位于 BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors 方法中,不过该方法实现比较冗长,但是逻辑却很清晰,所以这里主要概括一下其执行流程:

  1. 获取所有类型 bean 的 beanName
  2. 提供模板方法对 beanName 进行筛选
  3. 获取所有合法 bean 对应的 class 对象
  4. 检测当前 bean 是否被 @Aspect 注解,同时没有以 ajc$ 开头的字段
  5. 对于满足 4 的 bean,获取 bean 中定义的所有切点,并为每个切点生成对应的增强器
  6. 缓存解析得到的增强器,避免重复解析

上面的流程中我们重点看一下过程 5,该过程位于 ReflectiveAspectJAdvisorFactory#getAdvisors 方法中:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 获取切面 aspect 对应的 class 和 beanName
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    // 校验切面定义的合法性
    this.validate(aspectClass);

    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new ArrayList<>();
    // 1. 遍历处理切面中除被 @Pointcut 注解以外的方法
    for (Method method : this.getAdvisorMethods(aspectClass)) {
        // 获取切点配置信息,并生成对应的增强器
        Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // 2. 如果增强器不为空,同时又配置了增强延迟初始化,则需要追加实例化增强器
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    // 3. 获取所有引介增强定义
    for (Field field : aspectClass.getDeclaredFields()) {
        // 创建引介增强器
        Advisor advisor = this.getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    return advisors;
}

方法的整体执行流程如代码注释。拿到一个切面,方法首先会遍历获取切面中的增强方法,即被 @Around@Before@After@AfterReturning ,以及 @AfterThrowing 注解的方法,并调用 ReflectiveAspectJAdvisorFactory#getAdvisor 方法为每一个增强方法生成对应的增强器:

public Advisor getAdvisor(Method candidateAdviceMethod,
                              MetadataAwareAspectInstanceFactory aspectInstanceFactory,
                              int declarationOrderInAspect, String aspectName) {
    // 校验切面类定义的合法性
    this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

    // 获取注解配置的切点信息,封装成 AspectJExpressionPointcut 对象
    AspectJExpressionPointcut expressionPointcut = this.getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }

    // 依据切点信息生成对应的增强器
    return new InstantiationModelAwarePointcutAdvisorImpl(
            expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

方法 getAdvisor 首先对当前切面定义执行合法性校验,如果切面配置合法则获取当前方法上的切点注解定义,并封装成 AspectJExpressionPointcut 对象,该过程位于 ReflectiveAspectJAdvisorFactory#getPointcut 方法中,实现比较简单。

拿到切点注解定义之后,方法会依据切点的配置信息使用 InstantiationModelAwarePointcutAdvisorImpl 创建对应的增强器。类 InstantiationModelAwarePointcutAdvisorImpl 的实例化过程除了初始化了一些基本属性之外,主要是调用了 InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice 方法,依据增强类型对增强器实施相应的初始化操作:

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    Advice advice = this.aspectJAdvisorFactory.getAdvice(
            this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    return (advice != null ? advice : EMPTY_ADVICE);
}

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
                        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    // 获取切面 class 对象,并校验切面定义
    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    this.validate(candidateAspectClass);

    // 获取当前切点方法的注解定义信息
    AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // If we get here, we know we have an AspectJ method.
    // Check that it's an AspectJ-annotated class
    if (!this.isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
    }

    // 依据切点注解类型使用对应的增强器类进行封装
    AbstractAspectJAdvice springAdvice;
    switch (aspectJAnnotation.getAnnotationType()) {
        case AtPointcut: // @Pointcut
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        case AtAround: // @Around
            springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtBefore: // @Before
            springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfter: // @After
            springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfterReturning: // @AfterReturning
            springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;
        case AtAfterThrowing: // @AfterThrowing
            springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
        default:
            throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
    }

    // Now to configure the advice...
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();

    return springAdvice;
}

方法的整体执行流程见代码注释,逻辑比较清晰,Spring 会依据具体的增强注解类型,选择相应的增强器对切点定义进行封装。这里我们以 @Before 为例说明一下增强的执行流程,AspectJMethodBeforeAdvice 增强器关联注册的处理器是 MethodBeforeAdviceInterceptor,当我们调用一个被前置增强的目标方法时, MethodBeforeAdviceInterceptor#invoke 方法会被触发:

public Object invoke(MethodInvocation mi) throws Throwable {
    // 执行增强方法
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    // 执行目标方法
    return mi.proceed();
}

而这里执行的增强方法就对应着 AspectJMethodBeforeAdvice#before 方法,在 before 方法中会依据切点配置将相应的参数绑定传递给我们自定义的增强方法,并最终通过反射调用触发执行。

上面分析了普通方法级别增强的处理过程,对于另外一类增强(引介增强),方法 ReflectiveAspectJAdvisorFactory#getAdvisors 则使用专门的 DeclareParentsAdvisor 类型创建对应的增强器:

// 3. 获取所有引介增强定义
for (Field field : aspectClass.getDeclaredFields()) {
    // 创建引介增强器
    Advisor advisor = this.getDeclareParentsAdvisor(field);
    if (advisor != null) {
        advisors.add(advisor);
    }
}

private Advisor getDeclareParentsAdvisor(Field introductionField) {
    // 获取 @DeclareParents 注解定义
    DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
    if (declareParents == null) {
        return null;
    }

    // 没有指定默认的接口实现类
    if (DeclareParents.class == declareParents.defaultImpl()) {
        throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
    }

    // 使用 DeclareParentsAdvisor 类型创建对应的引介增强器
    return new DeclareParentsAdvisor(
            introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

对于引介增强来说,Spring 会注解 DelegatePerTargetObjectIntroductionInterceptor 处理器对其进行专门的处理,思想上与前面分析前置增强大同小异,这里就不再展开。

2.1.2 筛选适用于当前 bean 的增强器

继续回到 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors 方法,上面的过程我们分析了获取所有类型增强器的过程,但是这些增强器不一定都适用于当前 bean,我们需要依据切点配置信息对其进行筛选,这一过程位于 AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply 方法中:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    // 1. 处理引介增强器
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }

    // 2. 处理其他类型的增强器
    boolean hasIntroductions = !eligibleAdvisors.isEmpty(); // 表示是否含有引介增强
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // 引介增强已经处理过,这里直接跳过
            continue;
        }
        // 处理其他类型的增强器
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

方法首先会使用类过滤器(ClassFilter)筛选引介增强器,除了我们手动注册的类过滤器外,这里默认还会使用 TypePatternClassFilter 类过滤器执行过滤操作。然后方法会过滤筛选其它类型的增强器,这里除了使用类过滤器外,考虑方法级别增强的定义形式,还会使用方法匹配器(MethodMatcher)进行筛选,如果增强器适用于当前 bean,则将其加入到集合中用于下一步为当前 bean 创建增强代理对象,如果没有任何一个增强器适用于当前 bean,则方法会返回值为 null 的 DO_NOT_PROXY 数组对象,表示当前 bean 不需要被增强。

2.2 为当前 bean 创建增强代理对象

完成了对于当前 bean 增强器的筛选,接下来我们继续回到 AbstractAutoProxyCreator#wrapIfNecessary 方法中,看一下基于当前 bean 的增强器为 bean 创建增强代理对象的过程,该过程位于 AbstractAutoProxyCreator#createProxy 方法中:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                             @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();

    // 复制当前类实例的属性
    proxyFactory.copyFrom(this);

    // 如果没有指定 proxy-target-class = true
    if (!proxyFactory.isProxyTargetClass()) {
        // 检测当前 bean 是否应该基于类生成代理对象,即包含 preserveTargetClass=true 的属性,而不是他的接口代理
        if (this.shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        } else {
            // 如果是基于接口生成代理,则添加需要代理的接口(除容器 callback 接口、语言内在接口,以及标记接口)
            this.evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 将拦截器封装成 Advisor 对象
    Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors); // 添加增强器
    proxyFactory.setTargetSource(targetSource);
    // 定制代理工厂,模板方法
    this.customizeProxyFactory(proxyFactory);

    // 设置代理工厂被配置之后是否还允许修改,默认为 false,表示不允许修改
    proxyFactory.setFrozen(this.freezeProxy);
    if (this.advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 由 ProxyFactory 创建代理类
    return proxyFactory.getProxy(this.getProxyClassLoader());
}

方法的执行流程如代码注释。下面我们主要分析将拦截器封装成 Advisor 对象的过程,以及基于 ProxyFactory 创建增强代理对象的过程。

Spring 整个框架中定义了非常多的拦截器、增强器,以及增强方法等,这里通过 AbstractAutoProxyCreator#buildAdvisors 方法统一将他们封装成 Advisor 对象,从而简化代理的创建过程,封装的核心步骤如下(位于 DefaultAdvisorAdapterRegistry#wrap 方法中):

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    if (adviceObject instanceof Advisor) {
        // 如果已经是 Advisor,则无需多做处理
        return (Advisor) adviceObject;
    }
    if (!(adviceObject instanceof Advice)) {
        // 必须是 Advice 类型
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    if (advice instanceof MethodInterceptor) {
        // 如果是 MethodInterceptor,则直接使用 DefaultPointcutAdvisor 进行包装
        return new DefaultPointcutAdvisor(advice);
    }
    // 否则遍历注册的适配器,如果存在支持的适配器则使用 DefaultPointcutAdvisor 进行包装
    for (AdvisorAdapter adapter : this.adapters) {
        // Check that it is supported.
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

接下来我们重点看一下通过代理工厂获取增强代理对象的过程,位于 ProxyFactory#getProxy 方法中:

public Object getProxy(@Nullable ClassLoader classLoader) {
    return this.createAopProxy().getProxy(classLoader);
}

该方法拆分成了两个步骤:步骤 1 是创建 AOP 代理,Spring 默认提供了两种 AOP 代理实现,即 java 原生代理和 CGLib 代理;步骤 2 即依据相应的 AOP 代理创建目标类的增强代理对象。我们首先来看一下步骤 1 的实现:

// org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() // 是否对代理策略进行优化
            || config.isProxyTargetClass() // 用户指定使用 CGLib 生成代理对象
            || this.hasNoUserSuppliedProxyInterfaces(config)) { // 当前类没有接口定义,不得不使用 CGLib
        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)) {
            // 如果目标类是接口或代理类,则使用 java 原生代理
            return new JdkDynamicAopProxy(config);
        }
        // 使用 CGLib 生成代理
        return new ObjenesisCglibAopProxy(config);
    } else {
        // 使用 java 原生代理
        return new JdkDynamicAopProxy(config);
    }
}

这部分代码清晰说明了 spring 在生成代理对象时如何在 java 原生代理和 CGLib 代理之间进行选择,可以概括如下:

proxy-target-class=true

下面分别对基于 java 原生代理和 CGLib 代理生成增强代理对象的过程进行分析。

2.2.1 基于 java 原生代理创建增强代理对象

首先来看一下基于 java 原生代理生成增强代理对象的过程,位于 JdkDynamicAopProxy 类中。Java 原生代理要求代理类实现 InvocationHandler 接口,并在该接口声明的 invoke 方法中实现代理增强逻辑,而 JdkDynamicAopProxy 正好实现了该接口,对应的 JdkDynamicAopProxy#invoke 方法实现如下:

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

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 当前为 equals 方法,但是被代理类接口中未声明 equals 方法
            return this.equals(args[0]);
        } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 当前为 hashCode 方法,但是被代理类接口中未声明 hashCode 方法
            return this.hashCode();
        } else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // 如果是 DecoratingProxy 中定义的方法(即 getDecoratedClass),直接返回目标类对象
            return AopProxyUtils.ultimateTargetClass(this.advised);
        } else if (!this.advised.opaque // 允许被转换成 Advised 类型
                && method.getDeclaringClass().isInterface() // 接口
                && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // 方法所在类是 Advised 类及其父类
            // 直接反射调用该方法
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal; // 结果值

        // 指定内部间调用也需要代理
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

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

        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        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) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method is type-compatible.
            // Note that we can't help if the target sets a reference to itself in another returned object.
            retVal = proxy;
        } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    } finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

从上面的方法实现中,我们可以概括出整个增强代理的执行过程如下:

  1. 特殊处理 equals、hashCode、getDecoratedClass,以及 Advised 类及其父类中定义的方法
  2. 如果配置了 expose-proxy 属性,则记录当前代理对象,以备在内部间调用时实施增强
  3. 获取当前方法的拦截器链
  4. 如果没有拦截器定义,则直接反射调用增强方法,否则先逐一执行拦截器方法,最后再应用增强方法
  5. 处理返回值

下面我们重点来看一下步骤 4 中应用拦截器方法的实现,位于 ReflectiveMethodInvocation#proceed 方法中:

public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 如果所有的增强都执行完成,则执行增强方法
        return this.invokeJoinpoint();
    }

    // 获取下一个需要执行的拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 动态拦截器:执行动态方法匹配
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            // 动态匹配成功,执行对应的拦截方法
            return dm.interceptor.invoke(this);
        } else {
            // 动态匹配失败,忽略当前拦截器方法,递归执行下一个拦截器
            return this.proceed();
        }
    } else {
        // 普通拦截器:直接应用拦截方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

拦截器方法的执行流程如上述代码注释,是一个递归调用的过程,并在最后应用增强方法。

最后再来看一下 JdkDynamicAopProxy#getProxy 方法,毕竟创建完成的代理还是需要调用该方法进行获取:

public Object getProxy(@Nullable ClassLoader classLoader) {
    // 获取需要被代理的接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // 检测是否在被代理接口中声明了 equals 和 hashCode 方法
    this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // 基于 java 原生代理生成代理对象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

这里的逻辑即 java 原生代理的模板代码,如果对 java 代理比较熟悉的话,应该不难理解。

2.2.2 基于 CGLib 代理创建增强代理对象

接下来看一下基于 CGLib 代理生成增强代理对象的过程,位于 ObjenesisCglibAopProxy 类中,该类继承自 CglibAopProxy 类。获取 CGLib 代理类对象的方法 getProxy 定义在 CglibAopProxy 中,该方法基于 CGLib 的 Enhancer 类创建代理对象,属于 CGLib 的标准使用模式,因为有多个 callback 实现,所以这里使用了 CallbackFilter 模式,依据场景选择并应用对应的 callback 拦截器。

我们重点关心 callback 的实现,位于 CglibAopProxy#getCallbacks 方法中,受制于 CGLib 在执行时一次只允许应用一个 callback 实现,所以该方法依据参数配置实现了一组 callback,以覆盖不同的场景,而核心的 AOP callback 实现是 DynamicAdvisedInterceptor 类,它实现了 MethodInterceptor 接口,对应的 DynamicAdvisedInterceptor#intercept 方法实现如下:

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 {
        // 指定内部间调用也需要代理
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 获取当前方法的拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal; // 结果值
        // Check whether we only have one InvokerInterceptor: that is,
        // no real advice, but just reflective invocation of the target.
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // 拦截器链为空,则直接反射调用增强方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        } else {
            // 否则需要创建对应的 MethodInvocation,以链式调用拦截器方法,以及增强方法
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        // 处理返回值
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    } finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

可以看出该方法在实现流程上与前面介绍的 JdkDynamicAopProxy#invoke 方法是一致的,这是这里是基于 CGLib 实现而已。

三. 总结

最后我们对 Spring AOP 的运行机制进行一个总结,Spring AOP 的实现本质上是一个动态代理的过程,Spring 引入了 java 原生代理和 CGLib 代理,并依据场景选择相应的代理实现。Spring 容器在启动时会对每一个 bean 执行初始化,而 AOP 初始化的过程就发生在 bean 的后置初始化阶段,概括为:

  1. 从容器中获取所有的切面定义
  2. 为当前 bean 选择适用于当前 bean 的增强器集合
  3. 依据增强器集合基于动态代理机制生成相应的增强代理对象

当我们在调用一个被增强的方法时,相应的拦截器会依据连接点的方位在适当的位置触发对应的增强定义,从而最终实现 AOP 中定义的各类增强语义。

转载声明 : 版权所有,商业转载请联系作者,非商业转载请注明出处

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入理解C#(第3版)

深入理解C#(第3版)

斯基特 (Jon Skeet) / 姚琪琳 / 人民邮电出版社 / 2014-4-1 / 99.00元

本书是世界顶级技术专家“十年磨一剑”的经典之作,在C#和.NET领域享有盛誉。与其他泛泛介绍C#的书籍不同,本书深度探究C#的特性,并结合技术发展,引领读者深入C#的时空。作者从语言设计的动机出发,介绍支持这些特性的核心概念。作者将新的语言特性放在C#语言发展的背景之上,用极富实际意义的示例,向读者展示编写代码和设计解决方案的最佳方式。同时作者将多年的C#开发经验与读者分享,读者可咀其精华、免走弯......一起来看看 《深入理解C#(第3版)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具