Spring-aop源码解析

前言

spring的aop的源码相对来说比较少。但是很多细节想要弄懂,还是要在debug的世界里遨游很长一段时间。这里我分享一下我理解的aop的主要流程,希望对大家有所帮助。如有错误,请不吝指正。

核心原理

原理很简单,利用beanPostProcessor的postProcessAfterInitialization方法,在每一个bean初始化完成以后。判断当前bean是否有aop增强。如果有,则返回一个代理类作为bean。

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

看起来是不是瞬间懂了,但是我们要弄懂的地方还有很多。

是哪个beanPostProcessor

这一段代码是AbstractAutoProxyCreator类里的代码,真正执行这一段代码的是AnnotationAwareAspectJAutoProxyCreator,继承关系如下图所示:


image.png

什么时候注入到ioc容器的beanPostProcessors缓存的

这个AnnotationAwareAspectJAutoProxyCreator是什么时候被注入到ioc容器的。要分为两种情况

第一种,自动配置或者注解的形式

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
        AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true",
        matchIfMissing = true)
public class AopAutoConfiguration {

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = false)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",
            havingValue = "false", matchIfMissing = false)
    public static class JdkDynamicAutoProxyConfiguration {

    }

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",
            havingValue = "true", matchIfMissing = true)
    public static class CglibAutoProxyConfiguration {

    }

}

在springboot的自动配置的包下,有一个AopAutoConfiguration配置,根据@ConditionalOnClass条件可以看到,当有@EnableAspectJAutoProxy注解,@Aspect注解, 实现了Advice接口的容器类时。aop注解自动开启。所以没必要非得在启动类上加@EnableAspectJAutoProxy注解,存在@Aspect的切面,并且这个切面注入到ioc容器,也就是加上@Component注解,aop就已经开启了。当然,也可以配置里配置开启。
条件满足,就会注册AopAutoConfiguration,然后,在@EnableAspectJAutoProxy注解里,会@Import(AspectJAutoProxyRegistrar.class),之后AspectJAutoProxyRegistrar会把AnnotationAwareAspectJAutoProxyCreator注册到beanDefinitionMap。

第二种方式 xml配置的方式

当在xml文件里配置了时,解析器首先根据前缀aop,在spring的aop包下的META-INF/spring.handlers文件里找到对应的http://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler,然后执行AopNamespaceHandler的init()方法,再根据后面的aspectj-autoproxy,注册一个AspectJAutoProxyBeanDefinitionParser,如下图所示:

    @Override
    public void init() {
        // In 2.0 XSD as well as in 2.1 XSD.
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

然后在AspectJAutoProxyBeanDefinitionParser的parse()方法里,会把AnnotationAwareAspectJAutoProxyCreator注册到beanDefinitionMap。

实例化

接下来,在ioc容器执行registerBeanPostProcessors(beanFactory)方法时,把AnnotationAwareAspectJAutoProxyCreator实例化并放到beanPostProcessors缓存中。
这里有个地方要注意,初始化的时候因为也实现了BeanFactoryAware接口,所以会触发setBeanFactory()方法,在setBeanFactory()方法里又initBeanFactory((ConfigurableListableBeanFactory) beanFactory),初始化了advisorRetrievalHelper和aspectJAdvisorsBuilder。但是aop的各类缓存里是没有一起初始化的,只是个空壳子。

如何生成代理类

wrapIfNecessary

    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // 这个是为了防止循环依赖重复aop代理
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 判断当前bean是否已经处理过
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        // 这个缓存是缓存了bean是否需要代理,key为beanName,value是boolean值 
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        // 判断是否是内部类,是否需要跳过。
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
        // 根据beanName拿到需要增强的点
        // 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.getClass(), beanName, null);

最终后走到这个地方,为了篇幅,中间的代码有的会省略,建议打开源码和文章一起看

    @Override
    protected List findCandidateAdvisors() {
        // Add all the Spring advisors found according to superclass rules.
        // 查找当前 bean 工厂中所有符合条件的 Advisor bean,忽略 FactoryBeans 并排除当前正在创建的 bean。这里其实找不到啥。重点是下面
        List advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        if (this.aspectJAdvisorsBuilder != null) {
            //  这个里面缓存了增强的点。
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }

buildAspectJAdvisors

这段代码很简单。拿出所有的beanName,一个一个判断是不是被@Aspect修饰,如果是,拿出所有的method,然后判断是不是@before,@after,@around。是就封装,然后以类为单位放到缓存,key是beanName,value是增强的方法数组。

public List buildAspectJAdvisors() {
        List aspectNames = this.aspectBeanNames;

        if (aspectNames == null) {
            synchronized (this) {
                aspectNames = this.aspectBeanNames;
                if (aspectNames == null) {
                    List advisors = new ArrayList<>();
                    aspectNames = new ArrayList<>();
                    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, true, false);
                    for (String beanName : beanNames) {
                        if (!isEligibleBean(beanName)) {
                            continue;
                        }
                        // We must be careful not to instantiate beans eagerly as in this case they
                        // would be cached by the Spring container but would not have been weaved.
                        Class beanType = this.beanFactory.getType(beanName);
                        if (beanType == null) {
                            continue;
                        }
                        if (this.advisorFactory.isAspect(beanType)) {
                            aspectNames.add(beanName);
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                MetadataAwareAspectInstanceFactory factory =
                                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                List classAdvisors = this.advisorFactory.getAdvisors(factory);
                                if (this.beanFactory.isSingleton(beanName)) {
                                    this.advisorsCache.put(beanName, classAdvisors);
                                }
                                else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }
                                advisors.addAll(classAdvisors);
                            }
                            else {
                                // Per target or per this.
                                if (this.beanFactory.isSingleton(beanName)) {
                                    throw new IllegalArgumentException("Bean with name '" + beanName +
                                            "' is a singleton, but aspect instantiation model is not singleton");
                                }
                                MetadataAwareAspectInstanceFactory factory =
                                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                this.aspectFactoryCache.put(beanName, factory);
                                advisors.addAll(this.advisorFactory.getAdvisors(factory));
                            }
                        }
                    }
                    this.aspectBeanNames = aspectNames;
                    return advisors;
                }
            }
        }

        if (aspectNames.isEmpty()) {
            return Collections.emptyList();
        }
        List advisors = new ArrayList<>();
        for (String aspectName : aspectNames) {
            List cachedAdvisors = this.advisorsCache.get(aspectName);
            if (cachedAdvisors != null) {
                advisors.addAll(cachedAdvisors);
            }
            else {
                MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
        }
        return advisors;
    }

代码走到这里并不奇怪,但是奇怪的是,代码第一次走进来,advisorsCache和aspectBeanNames缓存里已经有值了,等于直接从缓存拿的,那么肯定是有一个执行了这个方法。通过调用链我们发现,shouldSkip()方法也执行了这段逻辑,那么问题就知道了,是在第一次执行shouldSkip()方法是,初始化了缓存。

shouldSkip()

问题又来了,第一次执行shouldSkip()是在哪?通过调用链我们发现,AnnotationAwareAspectJAutoProxyCreator还继承了InstantiationAwareBeanPostProcessor,这个BeanPostProcessor可以在bean实例化前后做一些逻辑。
最终,我们找到在第一次调用resolveBeforeInstantiation()的时候,触发了aop的初始化,具体在哪个时候呢,就是第一个创建bean调用到createBean()方法的时候,注意,并不是第一个bean创建的时候,有的bean创建可能不走createBean()方法。

createProxy()

有空再写。。。。

你可能感兴趣的:(Spring-aop源码解析)