spring-aop源码分析

Spring AOP源码解析

一、源码环境准备

1.1 jar包

新建maven工程,在pom.xml文件中加入下面内容:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

1.2 代码准备

因为主要是分析spring中源码,所以演示的代码很简单。主要包括下面四个文件

  1. 测试类

    测试类代码如下:

    public class App {
        public static void main( String[] args ) {
            AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainAop.class);
            MathCalculator calculator = ioc.getBean(MathCalculator.class);
            calculator.div(2, 2);
        }
    }
    

    即主要是新建IOC容器,获取业务类bean,然后调用业务方法

  2. 配置类

    @Configuration
    @EnableAspectJAutoProxy
    public class MainAop {
        @Bean
        public MathCalculator calculator() {
            return new MathCalculator();
        }
        @Bean
        public LogAspect logAspect() {
            return new LogAspect();
        }
    }
    

    注意配置类中要加入@EnableAspectJAutoProxy注解,这样子才能开启AOP代理,如果是在Spring boot中,可以不用加入此注解,因为Spring boot自动配置会加入此注解。

    然后给容器中加入了两个bean

  3. 业务类

    public class MathCalculator {
        public int div(int i, int j) {
            return i/j;
        }
    }
    

    就是一个除法运算

  4. 切面类

    @Aspect
    public class LogAspect {
        @Pointcut("execution(public int com.leon.aop.MathCalculator.*(..))")
        public void pointCut() {
        }
        @Before("pointCut()")
        public void logStart(JoinPoint joinPoint) {
            System.out.println("除法运行.....参数列表:{}");
        }
        @After("pointCut()")
        public void logEnd() {
            System.out.println("logEnd");
        }
        @AfterReturning("pointCut()")
        public void logReturn() {
            System.out.println("logReturn,return value:{}");
        }
        @AfterThrowing("pointCut()")
        public void logThrow() {
            System.out.println("logThrow");
        }
    }
    

    即添加对应的注解,进行切入点的切入。

二、源码分析

源码分析主要从下面4个方面来进行讲解

2.1 @EnableAspectJAutoProxy注解作用

1. 注解作用

进入注解源码,可以看到下面内容

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

可以看到采用Import注解加载了AspectJAutoProxyRegistrar类,然后进入AspectJAutoProxyRegistrar类,内容如下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * Register, escalate, and configure the AspectJ auto proxy creator based on the value
     * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
     * {@code @Configuration} class.
     */
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

2. AnnotationAwareAspectJAutoProxyCreator继承关系

可以看到AspectJAutoProxyRegistrar是一个BeanDefinitionRegistrar,即可以将BeanDefinition信息加载到容器中(BeanDefinition是bean对象的一种静态描述,spring会根据BeanDefinition来创建bean对象)。在代码第一行,是调用registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,此方法就是将AnnotationAwareAspectJAutoProxyCreator这个类的BeanDefinition放置到BeanDefinitionRegistry中。AnnotationAwareAspectJAutoProxyCreator类的继承关系如下:

AnnotationAwareAspectJAutoProxyCreator
    --> AspectJAwareAdvisorAutoProxyCreator
        -->AbstractAdvisorAutoProxyCreator
            -->AbstractAutoProxyCreator
                -->ProxyProcessorSupport
                    -->ProxyConfig
                    -->Ordered(接口)
                    -->BeanClassLoaderAware(接口)
                    -->AopInfrastructureBean(接口)
                -->SmartInstantiationAwareBeanPostProcessor(接口)
                    -->InstantiationAwareBeanPostProcessor(接口)
                        -->BeanPostProcessor(接口)
                -->BeanFactoryAware(接口)
                    -->Aware(接口)

通过分析类的继承路径,当前AnnotationAwareAspectJAutoProxyCreator具有以下几种能力

  • InstantiationAwareBeanPostProcessor,即可以对bean创建前进行处理
  • BeanPostProcessor,即bean初始化前后进行处理
  • BeanFactoryAware,可以获取到当前IOC容器
  • BeanClassLoaderAware,可以获取到当前类加载器
  • Ordered,排序需要

3. 总结

@EnableAspectJAutoProxy注解给容器中添加了AnnotationAwareAspectJAutoProxyCreator类的定义,此类具有bean后置处理器的作用,所以猜想AOP应该是在bean创建前后来产生代理对象。AnnotationAwareAspectJAutoProxyCreator当前在容器中仅仅是BeanDefinition,真正创建实例加载到容器中是在AbstractApplicationContext中的refresh(注:此方法是spring中核心方法,后置处理器,事件监听注册,bean实例化都是在这个模板方法中进行的)中的registerBeanPostProcessors(beanFactory)方法中。这里就不具体分析了。refresh方法如下:

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
                // Initialize message source for this context.
                initMessageSource();
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
                // Initialize other special beans in specific context subclasses.
                onRefresh();
                // Check for listener beans and register them.
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                finishRefresh();
            }
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
                // Reset 'active' flag.
                cancelRefresh(ex);
                // Propagate exception to caller.
                throw ex;
            }
            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

2.2 切面类bean实例的创建过程(即LogAspect)

bean实例的创建是在refresh中的finishBeanFactoryInitialization(beanFactory);方法中

bean实例创建步骤, 简单来说,依次会执行以下方法

  1. 调用getBean(AbstractBeanFactory)获取logAspect对象

  2. doGetBean,获取bean对象

    记录当前bean在已经创建成功的集合中

  3. createBean

    1. 调用resolveBeforeInstantiation方法,方法内容如下:

      protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
            Object bean = null;
            if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
                // Make sure bean class is actually resolved at this point.
                if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                    Class<?> targetType = determineTargetType(beanName, mbd);
                    if (targetType != null) {
                        bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                        if (bean != null) {
                            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                        }
                    }
                }
                mbd.beforeInstantiationResolved = (bean != null);
            }
            return bean;
        }
      

      applyBeanPostProcessorsBeforeInstantiation中会依次调用每个后置处理器的postProcessBeforeInstantiation方法(注:此方法与BeanPostProcessor中的postProcessBeforeInitialization是由区别的,postProcessBeforeInitialization是在bean初始化前被调用)。

      但是经过代码运行,resolveBeforeInstantiation方法针对logAspect会返回null

    2. 调用doCreateBean方法,方法内逻辑如下

      • 首先创建logAspect实例,给实例中的属性设置值
      • initializeBean方法,初始化方法,在调用bean的初始化前后会分别调用postProcessBeforeInitialization和postProcessAfterInitialization

总结

logAspect实例不需要创建其对应的代理对象,这里只是描述了bean实例的创建过程。在下面介绍MathCalculator实例的创建过程中也会经历这些阶段,但是在postProcessAfterInitialization中会创建其代理对象并方法

2.3 业务类bean实例创建过程(即MathCalculator)

MathCalculator的创建类似于logAspect实例的创建,主要的区别在initializeBean(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition))方法的applyBeanPostProcessorsAfterInitialization方法,下面会详细说明applyBeanPostProcessorsAfterInitialization方法的执行过程

1. applyBeanPostProcessorsAfterInitialization内容

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

从代码中可以看到,for循环获取所有的后置处理器并依次执行postProcessAfterInitialization方法。由于主要是分析AOP相关功能,所以这里只看AnnotationAwareAspectJAutoProxyCreator这个后置处理器(注:在IDEA中debug可以设置只在感兴趣的值进行断点设置)

2. postProcessAfterInitialization方法

AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator抽象类,并且调用的也是抽象类中的此方法

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

getCacheKey是获取当前bean在容器缓存中的key名称,然后调用wrapIfNecessary方法

3. wrapIfNecessary方法

部分代码如下:

// Create proxy if we have advice.
        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;

首先会调用getAdvicesAndAdvisorsForBean方法来获取当前待创建bean的advisors(可以理解为增强器)

3.1 获取当前bean的advice和advisor
    @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();
    }

findEligibleAdvisors方法获取到合适的增强器(当前的增强器就是logStart等四个)。如果没有增强器,则直接返回DO_NOT_PROXY(就是null),否则返回数组。

findEligibleAdvisors主要逻辑:

  • 找到被@Aspect注解标注的类,获取其中的切入点方法
  • 然后根据当前bean中方法来获取符合条件的切入点
  • 然后返回满足条件的切入点方法

findEligibleAdvisors返回值中包括五个,其中四个就是上面定义的logStart,logEnd,logReturn,logThrow。

3.2 创建代理对象(createProxy)

此方法就是利用JDK的动态代理或者CGLIB来创建代理对象,根据当前bean是否实现了接口来选择JDK还是CGLIB

2.4 业务类方法的执行(div方法的执行)

在演示的代码中,即MathCalculator calculator = ioc.getBean(MathCalculator.class);.这里获取到的bean就是代理对象。

div方法,被委托给代理对象,代理对象会根据切入点的顺序来依次调用切入方法。

spring4:正常情况,方法执行顺序 @Before ---》@After ----》@AfterReturn

异常情况,顺序@Before ---》@After---》@AfterThrow,

@Around方法较为特殊,会首先进入,具体执行顺序可以查看官网介绍

三、总结

本文介绍了spring 中aop实现的原理。主要思路就是利用后置处理器来生成对应的代理对象。

这里也介绍了@EnableAspectJAutoProxy的应用,类似于Spring中其他的@EnableXXX,也可以进行类似分析。

四、参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容