美文网首页java
Spring AOP源码解读1 - 程序入口

Spring AOP源码解读1 - 程序入口

作者: 未知海苔 | 来源:发表于2016-12-09 14:05 被阅读0次

    前言

    最近看了《从零开始写JavaWeb框架》,想比较一下Spring AOP的实现方式和书的上实现方式有什么不同,所以先把Spring AOP的源码读一下,再进行比较。

    Spring的源码实在是复杂,在读的过程中参考了很多书和网上的文章,本文算是这些文章的总结,再加上一些我自己对另个细节的理解。

    本文分成 3 部分:

    • 程序入口
    • 切面和增强的取得
    • 代理的生成

    一,注册AspectJAnnotationAutoProxyCreator

    如果使用<aop:aspectj-autoproxy />标签来自动生成代理的话,入口程序是AopNamespaceHandler。在AopNamespaceHandler中,下面一段代码是对<aop:aspectj-autoproxy />标签执行的调用:

    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    

    AspectJAutoProxyBeanDefinitionParser解析器中,首先调用的parse方法。parse方法中有一行代码:

    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    

    我们看一下registerAspectJAnnotationAutoProxyCreatorIfNecessary方法的实际内容:

    public static void registerAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {
        // 注册或更新 AutoProxyCreator 定义 beanName 为 org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
        // 如果internalAutoProxyCreator的BeanDefinition已经存在,而根据优先级更新BeanDefinition
        // 在这里我们注册的是AnnotationAwareAspectJAutoProxyCreator
        BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        // 对于 proxy-target-class 以及 expose-proxy 属性的处理
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        // 注册组件并通知,便于监听器作进一步处理
        // 其中 beanDefinition 的 className 为 AnnotationAwareAspectJAutoProxyCreator
        registerComponentIfNecessary(beanDefinition, parserContext);
    }
    



    那为什么注册AnnotationAwareAspectJAutoProxyCreator,注册AnnotationAwareAspectJAutoProxyCreator有什么用呢?

    其实,实现AOP处理是其实是通过BeanPostProcessor机制实现的。AnnotationAwareAspectJAutoProxyCreator的父类也实现一个BeanPostProcessor类型的接口,而生成代理的逻辑就在AnnotationAwareAspectJAutoProxyCreator的BeanPostProcessor接口实现里面。
    更严谨地说,AnnotationAwareAspectJAutoProxyCreator的父类实现的接口是
    SmartInstantiationAwareBeanPostProcessor,主要是Spring框架内部使用的一个接口。而这个接口的父接口InstantiationAwareBeanPostProcessor 是实现代理的重点之一。

    这3个接口的关系如下:
    SmartInstantiationAwareBeanPostProcessor -> InstantiationAwareBeanPostProcessor -> BeanPostProcessor

    为什么说是是InstantiationAwareBeanPostProcessor接口的子接口,接口是重点之一?那InstantiationAwareBeanPostProcessor接口是什么接口呢?

    BeanPostProcessor主要作用于Bean实例化后,初始化前后。InstantiationAwareBeanPostProcessor虽然是BeanPostProcessor的子接口,但它的调用时间点其发生在Bean实例化前,在真正调用doCreate()创建bean实例之前。
    在创建Bean实例之前,会先调用resolveBeforeInstantiation方法,这个方法是生成Bean代理的地方。如果此方法返回值不为空则直接返回生成的Bean的代理,如果为空就向下走正常的Bean生成流程。

    spring注释“Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. ”给BeanPostProcessors一个机会返回代理proxy对象。

    InstantiationAwareBeanPostProcessor接口方法,就是在resolveBeforeInstantiation方法中调用的。所以可以看出,BeanPostProcessore有很多,但Spring AOP的实现就是通过InstantiationAwareBeanPostProcessor这个BeanPostProcessor实现的。看一下源码:

    protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {
        ......
        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            // 如果返回值不为空,说明生成成了此BeanName的代理,直接返回代理对象
            Object bean = resolveBeforeInstantiation(beanName, mbd);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            ......
        }
    
        // 如果没有生成代理对象,就按正常流程走,生成Bean对象
        Object beanInstance = doCreateBean(beanName, mbd, args);
        .....
        return beanInstance;
    }
    
    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) {
                    // 调用InstantiationAwareBeanPostProcessor接口的地方
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }
    
        protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName)
                throws BeansException {
    
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    // 转换成InstantiationAwareBeanPostProcessor接口,并调用
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                    if (result != null) {
                        return result;
                    }
                }
            }
            return null;
        }
    

    顺带说一下,注册AnnotationAwareAspectJAutoProxyCreator的目的是:把这个类的BeanDefinition通过registerBeanDefinition方法(DefaultListableBeanFactory类中)加入到beanDefinitionMap中,作为一个Bean让Spring管理,这样Spring就可以随意取得它了。



    如果使用<aop:aspectj-autoproxy />标签来自动生成代理的话,入口程序是AopNamespaceHandler。那AopNamespaceHandler 是在什么地方被调用的呢?
    这个问题让我们从容器启动的地方开始说明。以FileSystemXmlApplicationContext 为例,这个类的入口是构造函数里面的refresh() 方法。从refresh() 方法开始,调用流程是这样的:(以下流程全部是嵌套调用的关系)

    1. refresh() ->
       刷新容器 
    2. obtainFreshBeanFactory() ->
       获得刷新后的Bean容器
    3. refreshBeanFactory() ->
       刷新Bean容器
    4. loadBeanDefinitions() ->
       加载BeanDefinition
    5. XmlBeanDefinitionReader#loadBeanDefinitions
       新建一个XmlBeanDefinitionReader实例(new XmlBeanDefinitionReader(beanFactory)),调用这个实例的loadBeanDefinitions方法。
    6. XmlBeanDefinitionReader#doLoadBeanDefinitions
    7. XmlBeanDefinitionReader#registerBeanDefinitions
    8. XmlBeanDefinitionReader#createBeanDefinitionDocumentReader
       在这个方法中取得了DefaultBeanDefinitionDocumentReader实例。接下来调用这个实例的方法。
    9. DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
       在这个方法中,根据URI判断是否使用AopNamespaceHandler
    10. DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
       从registerBeanDefinitions方法开始,内部连续调用一系列方法,一直调用到parseBeanDefinitions方法。在这个方法中,根据XML文件的URI判断使用哪些解析器,例如是<Beans>类标签解析器,还是<aop>类标签解析器。
       如果是需要<aop>标签解析器的话,在this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)代码中,从配置文件(spring.handlers)从取出AopNamespaceHandler的类名,生成这个类的实例,然后调用这个类的parse方法。
    11. AopNamespaceHandler#parse
       这个方法的功能是,根据具体标签调用具体解析器的parse方法。
       - <aop:aspectj-autoproxy>:AspectJAutoProxyBeanDefinitionParser
       - <aop:config>:ConfigBeanDefinitionParser
       等。
       这个方法的调用,又回到了我们最初讲的AopNamespaceHandler入口的地方。
    
    到此为止,从容器到AopNamespaceHandler类调用的过程也讲完了。
                
    

    二,AspectJAnnotationAutoProxyCreator的流程

    通过上面的内容,我们知道了注册AnnotationAwareAspectJAutoProxyCreator的意义,并且知道了生成代理是在它的BeanPostProcessor接口里做的,现在看看被实现的接口的内容。(postProcessAfterInitialization 具体实现是在其父类 AbstractAutoProxyCreator 中完成的):

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       if (bean != null) {
          // 根据给定的 bean 的 class 和 name 构建出个 key,格式:beanClassName_beanName
          Object cacheKey = getCacheKey(bean.getClass(), beanName);
          if (!this.earlyProxyReferences.contains(cacheKey)) {
             // 如果它适合被代理,则需要封装指定 bean。
             return wrapIfNecessary(bean, beanName, cacheKey);
          }
       }
       return bean;
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
       // 是否已经处理过
       if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
          return bean;
       }
       // 无需增强
       if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
          return bean;
       }
       // 给定的 bean 类是否代表一个基础设施类,基础设施类不应代理,或者配置了指定 bean 不需要自动代理
       if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
          this.advisedBeans.put(cacheKey, Boolean.FALSE);
          return 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;
    }
    

    这里有两个方法特别重要:

    1. getAdvicesAndAdvisorsForBean:如果Bean是要被代理的对象的话,取得Bean相关的Interceptor
    2. createProxy:创建代理

    下一篇文章,我们就这两个方法的下面的流程分开来分析一下,首先分析getAdvicesAndAdvisorsForBean相关代码。




    关于接口实现的补充:

    AnnotationAwareAspectJAutoProxyCreator 一共实现了2个BeanPostProcessor 的接口,的4个方法:

    • postProcessBeforeInstantiation(InstantiationAwareBeanPostProcessor)
    • postProcessAfterInstantiation(InstantiationAwareBeanPostProcessor)
    • postProcessBeforeInitialization(BeanPostProcessor)
    • postProcessAfterInitialization(BeanPostProcessor)

    postProcessBeforeInstantiationInstantiationAwareBeanPostProcessor接口)方法中,这个方法是在AnnotationAwareAspectJAutoProxyCreator的基类AbstractAutoProxyCreator中实现的。细看一下,在postProcessAfterInitialization方法中也有类似的生成代理的代码。这是为什么呢?

    上网找了一些资料,在postProcessBeforeInstantiation方法中有一个判断:

    如果某个Bean设置了自定义TargetSource的话,就在本方法中进行生成代理

    postProcessAfterInitialization则没有这样的判断,只是在生成代理前判断了一下代理是否已经生成。具体为什么有这样的必须还不清楚(以后有需要调查一下),但结果就是:

    • 如果Bean设置了自定义TargetSource,就在postProcessBeforeInstantiation中生成代理
    • 如果没有,就在postProcessAfterInitialization中生成代理。

    最后,不管理在哪个方法里生成代理,在创建每个Bean时都会被调用这两个方法,代理的生成逻辑就是在这两个方法中实现的。

    关于TargetSource:spring-aop组件详解——TargetSource目标源
    关于自定义TargetSource:《Spring揭密》的9.6章节

    相关文章

      网友评论

        本文标题:Spring AOP源码解读1 - 程序入口

        本文链接:https://www.haomeiwen.com/subject/kxiemttx.html