loadBeanDefinitions方法源码跟踪(三)

因为字数超过了限制,所以分成了三篇,承接上篇:
https://www.jianshu.com/p/46e27afd7d96

代码过宽,可以shift + 鼠标滚轮 左右滑动查看

4.parseCustomElement

<!-- 事务处理,事务只针对service层 -->
<aop:config>
    <aop:pointcut id="pc" expression="execution(public * cn.mrdear.service.impl..*(..))" />
    <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
</aop:config>

非默认名称空间的标签,走的是这个方法

//4.如果该标签属于其他的名称空间比如:context,aop等
//xmlns:aop="http://www.springframework.org/schema/aop"
//xmlns:context="http://www.springframework.org/schema/context"
delegate.parseCustomElement(ele);

进入这个方法,在BeanDefinitionParserDelegate类中

public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
}


public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    
    //拿到标签对应的名称空间URI
    //比如:http://www.springframework.org/schema/aop
    String namespaceUri = getNamespaceURI(ele);
    
    //4.1拿到名称空间处理器解析器,去解析URI,获取名称空间处理器
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    
    //4.2通过处理器的某个解析器解析对应标签
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

4.1 resolve

DefaultNamespaceHandlerResolver是名称空间处理器解析器的默认实现

跟踪标记4.1的方法

//4.1拿到名称空间处理器解析器,去解析URI,获取名称空间处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);


/**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
*
* 根据提供的 namespace URI 从配置映射中找到 NamespaceHandler
*/
@Override
public NamespaceHandler resolve(String namespaceUri) {
    
    //拿到处理器解析器中所有的处理器
    Map<String, Object> handlerMappings = getHandlerMappings();
    
    //拿到指定的处理器
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    
    //如果为null就返回null
    if (handlerOrClassName == null) {
        return null;
    }
    
    //如果是实例就返回实例
    else if (handlerOrClassName instanceof NamespaceHandler) {
        return (NamespaceHandler) handlerOrClassName;
    }
    
    //否则就一定是字符串
    else {
        String className = (String) handlerOrClassName;
        try {
            
            //通过加载器生成处理器的Class对象
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            
            //必须要实现NamespaceHandler接口
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                                             "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            }
            
            //实例化一个
            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            
            //初始化,进入这个方法,里面初始化多个解析器
            namespaceHandler.init();
            
            //用实例覆盖原先handlerMappings中的字符串
            handlerMappings.put(namespaceUri, namespaceHandler);
            return namespaceHandler;
        }
        catch (ClassNotFoundException ex) {
            throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                                         namespaceUri + "] not found", ex);
        }
        catch (LinkageError err) {
            throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                                         namespaceUri + "]: problem with handler class file or dependent class", err);
        }
    }
}



/**
* 这个方法在类AopNamespaceHandler中
*
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*
* 为config、spring-configured、aspectj-autoproxy以及scoped-proxy标签注册BeanDefinitionParsers
*/
@Override
public void init() {
    
    //这里面每一个解析器都对应着AOP名称空间下的一个标签
    //不同的处理器注册的解析器都不一样
    //这里随便找一个解析器,查看他的注册流程
    
    // 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());
}

/**
* 此方法在类AopNamespaceHandler的父类NamespaceHandlerSupport中
*
* Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
* handle the specified element. The element name is the local (non-namespace qualified)
* name.
*
* 子类可以调用这个方法注册指定的BeanDefinitionParser去处理相关的标签
* 这个elementName是个局部名称(不包含名称空间)
*/
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
    
    //将解析器注册到这个名称空间处理器的parsers属性中,
    //parsers是一个HashMap
    this.parsers.put(elementName, parser);
}

4.2 parse

跟踪标记4.2的方法

进入处理器的抽象父类NamespaceHandlerSupport中

入参传递了一个ParserContext解析器上下文,ParserContext中拿到了阅读器上下文的引用,代理的引用,containingBd在这里为null

//4.2通过处理器的某个解析器解析对应标签
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*
* 通过委派给根据对应标签所注册的解析器,来解析提供的标签
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    
    //将标签名称作为key,去处理器内部的parsers属性中,获取对应的value值,也就是解析器
    //利用解析器来解析这个标签,进入这个方法。
    return findParserForElement(element, parserContext).parse(element, parserContext);
}


/**
* 因为解析是的<aop:config>,所以该方法在ConfigBeanDefinitionParser类中
*
* Parse the specified {@link Element} and register the resulting
* {@link BeanDefinition BeanDefinition(s)} with the
* {@link org.springframework.beans.factory.xml.ParserContext#getRegistry() BeanDefinitionRegistry}
* embedded in the supplied {@link ParserContext}.
* <p>Implementations must return the primary {@link BeanDefinition} that results
* from the parse if they will ever be used in a nested fashion (for example as
* an inner tag in a {@code <property/>} tag). Implementations may return
* {@code null} if they will <strong>not</strong> be used in a nested fashion.
*
* 解析指定的标签,并注册最终的结果bean Definition到内嵌在ParserContext中的bean工厂中
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    
    //一个组件集合,里面包含了多个组件,以标签名(包含了名称空间)作为名称,比如aop:config
    //里面包含的多个组件就是aop:config的子标签,包含对标签属性的描述
    //比如xml中pointcut子标签就是一个组件,advisor子标签也是一个组件
    //这个组件记录了子标签的name属性或者expression属性,根据不同子标签,属性不一样
    CompositeComponentDefinition compositeDef =
        new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    
    //将组件集合保存在parserContext解析器上下文的containingComponents属性中
    parserContext.pushContainingComponent(compositeDef);
    
    //4.2.1配置自动代理创建器
    configureAutoProxyCreator(parserContext, element);
    
    //配置完创建器后拿到所有子标签
    List<Element> childElts = DomUtils.getChildElements(element);
    
    for (Element elt: childElts) {
        
        //拿到所有子标签localName,也就是不包括名称空间的标签名字
        String localName = parserContext.getDelegate().getLocalName(elt);
        
        //如果子标签是pointcut
        if (POINTCUT.equals(localName)) {
            
            //4.2.2解析pointcut子标签
            parsePointcut(elt, parserContext);
        }
        
        //如果子标签是advisor
        else if (ADVISOR.equals(localName)) {

            //4.2.3解析advisor子标签
            parseAdvisor(elt, parserContext);
        }
        
         //如果子标签是aspect
        else if (ASPECT.equals(localName)) {

            //4.2.4解析aspect子标签
            parseAspect(elt, parserContext);
        }
    }
    
    //4.2.5弹出组件
    parserContext.popAndRegisterContainingComponent();
    return null;
}

4.2.1 configureAutoProxyCreator

跟踪标记4.2.1的方法

此方法在类ConfigBeanDefinitionParser中实现

//4.2.1配置自动代理创建器
configureAutoProxyCreator(parserContext, element);

/**
* Configures the auto proxy creator needed to support the {@link BeanDefinition BeanDefinitions}
* created by the '{@code <aop:config/>}' tag. Will force class proxying if the
* '{@code proxy-target-class}' attribute is set to '{@code true}'.
* 
* 配置自动代理创建器,支持由<aop:config/>标签创建的beanDefinition
* 如果标签的proxy-target-class属性被设置为true,那么强制代理
*/
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
    AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}

/**
* 进入AopNamespaceUtils类中的静态方法
*/
public static void registerAspectJAutoProxyCreatorIfNecessary(
    ParserContext parserContext, Element sourceElement) {
    
   //4.2.1.1如果必要的话注册AspectJ自动代理创建器
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
        parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    
    //4.2.1.2如果必要的话使用CLass代理
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    
    //4.2.1.3如果必要的话注册组件
    registerComponentIfNecessary(beanDefinition, parserContext);
}
4.2.1.1 registerAspectJAutoProxyCreatorIfNecessary

先跟踪标记4.2.1.1的方法

该方法在AopConfigUtils类中实现

//4.2.1.1如果必要的话注册AspectJ自动代理创建器
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
    parserContext.getRegistry(), parserContext.extractSource(sourceElement));


public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    
    //传递了一个AspectJAwareAdvisorAutoProxyCreator的Class,进入这个方法
    return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}


private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   
    //工厂中是否已经注册了org.springframework.aop.config.internalAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
       
        //如果已经有注册了internalAutoProxyCreator,并且和入参传递的不是同一个Class,
        //那么就根据优先级进行选择
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            
            //类AopConfigUtils中有个ArrayList属性APC_PRIORITY_LIST,在类静态构造中依次加入了
            //几个创建器,这个方法就是查找某个创建器在APC_PRIORITY_LIST中的索引,如果没有找到就报错
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
           
            //internalAutoProxyCreator的索引为0,入参的AspectJAwareAdvisorAutoProxyCreator
            //索引为1,后者要大,所以重新设置下apcDefinition的beanClass
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        
        //直接返回null
        return null;
    }
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}
4.2.1.2 useClassProxyingIfNecessary

跟踪标记4.2.1.2

此方法的实现在AopNamespaceUtils类中

//4.2.1.2如果必要的话使用CLass代理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
    if (sourceElement != null) {
        
        //查看是否有设置proxy-target-class属性
        boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        
        //查看是否有设置expose-proxy属性
        boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}
4.2.1.3 registerComponentIfNecessary

跟踪标记4.2.1.3

此方法的实现在AopNamespaceUtils类中

//4.2.1.3如果必要的话注册组件
registerComponentIfNecessary(beanDefinition, parserContext);

private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
    
    //因为前面创建器中的beanClass用了新的那个,所以返回的beanDefinition为null,
    //这里的beanDefinition也就为null了,跳过
    if (beanDefinition != null) {
        BeanComponentDefinition componentDefinition =
            new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        parserContext.registerComponent(componentDefinition);
    }
}

4.2.2 parsePointcut

跟踪标记4.2.2

此方法的实现在ConfigBeanDefinitionParser类中

//4.2.2解析pointcut子标签
parsePointcut(elt, parserContext);

/**
* Parses the supplied {@code <pointcut>} and registers the resulting
* Pointcut with the BeanDefinitionRegistry.
* 
* 解析<pointcut>标签,注册结果到bean工厂中
*/
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    
    //拿到id和expression的属性
    String id = pointcutElement.getAttribute(ID);
    String expression = pointcutElement.getAttribute(EXPRESSION);

    AbstractBeanDefinition pointcutDefinition = null;

    try {
        
        //解析前放入,解析后弹出
        this.parseState.push(new PointcutEntry(id));
        
        //创建一个bean definition,提前指定AspectJExpressionPointcut.class为beanClass
        //默认多例,同步,将expression添加到bean definition的propertyValues中
        pointcutDefinition = createPointcutDefinition(expression);
       
        //这里走null
        pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
        
        String pointcutBeanName = id;
        
        //有无id走不同方法
        if (StringUtils.hasText(pointcutBeanName)) {
            
            //走的工厂注册方法,上面跟踪过,可以回看3.3.1标记
            parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
        }
        else {
            pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
        }
        
        //注册组件,进入这个方法查看
        parserContext.registerComponent(
            new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
    }
    finally {
        
        //解析前放入,解析后弹出
        this.parseState.pop();
    }

    return pointcutDefinition;
}

/**
* 此方法的实现在ParserContext类中
*/
public void registerComponent(ComponentDefinition component) {
    
    //拿到parserContext中containingComponents属性里的最后一个元素
    //这个元素是在4.2标记中parse方法里被放入,组件名称叫做aop:config
    CompositeComponentDefinition containingComponent = getContainingComponent();
    if (containingComponent != null) {
       
        //增加嵌套的组件,进入这个方法
        containingComponent.addNestedComponent(component);
    }
    else {
        this.readerContext.fireComponentRegistered(component);
    }
}

/**
* 这个方法在CompositeComponentDefinition类中
*
* Add the given component as nested element of this composite component.
*
* 增加指定的组件,作为这个组件的嵌套元素
*/
public void addNestedComponent(ComponentDefinition component) {
    Assert.notNull(component, "ComponentDefinition must not be null");
    
    //也就是在CompositeComponentDefinition类中的nestedComponents属性中,
    //将入参的component添加进去
    //nestedComponents是一个LinkedList
    this.nestedComponents.add(component);
}

4.2.3 parseAdvisor

跟踪标记4.2.3

此方法的实现在ConfigBeanDefinitionParser类中

//4.2.3如果子标签是advisor
parseAdvisor(elt, parserContext);

/**
* Parses the supplied {@code <advisor>} element and registers the resulting
* {@link org.springframework.aop.Advisor} and any resulting {@link org.springframework.aop.Pointcut}
* with the supplied {@link BeanDefinitionRegistry}.
*
* 解析 <advisor> 标签,并注册 Advisor 结果和其他 Pointcut 结果到工厂中
*/
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
    
    // 4.2.3.1 创建一个顾问的 BeanDefinition
    AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
    String id = advisorElement.getAttribute(ID);

    try {
        this.parseState.push(new AdvisorEntry(id));
        String advisorBeanName = id;
        if (StringUtils.hasText(advisorBeanName)) {
            
            // 注册到工厂
            parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
        }
        else {
            
            // 没有 id 属性,生成默认规则的名字再注册到工厂
            advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
        }

        // 4.2.3.2 解析切入点属性
        Object pointcut = parsePointcutProperty(advisorElement, parserContext);
        
        // advisorDef 添加 pointcut 等属性,然后将 advisorDef 注册到组件组合定义中
        if (pointcut instanceof BeanDefinition) {
            advisorDef.getPropertyValues().add(POINTCUT, pointcut);
            parserContext.registerComponent(
                new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
        }
        else if (pointcut instanceof String) {
            advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
            parserContext.registerComponent(
                new AdvisorComponentDefinition(advisorBeanName, advisorDef));
        }
    }
    finally {
        this.parseState.pop();
    }
}
4.2.3.1 createAdvisorBeanDefinition

跟踪标记4.2.3.1

此方法的实现在ConfigBeanDefinitionParser类中

// 4.2.3.1 创建一个顾问的 BeanDefinition
AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);


/**
* Create a {@link RootBeanDefinition} for the advisor described in the supplied. Does <strong>not</strong>
* parse any associated '{@code pointcut}' or '{@code pointcut-ref}' attributes.
*
* 创建一个 advisor 的 RootBeanDefinition,不解析任何关联的 pointcut 或 pointcut-ref 属性
*/
private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
    RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
    advisorDefinition.setSource(parserContext.extractSource(advisorElement));

    // 拿到 advisor 标签上的 advice-ref 属性
    String adviceRef = advisorElement.getAttribute(ADVICE_REF);
    if (!StringUtils.hasText(adviceRef)) {
        parserContext.getReaderContext().error(
            "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
    }
    else {
        
        // 添加到属性键值对集合中
        advisorDefinition.getPropertyValues().add(
            ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
    }

    // 拿到标签上的 order 属性
    if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
        
        // 添加到属性键值对集合中
        advisorDefinition.getPropertyValues().add(
            ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
    }

    return advisorDefinition;
}
4.2.3.2 parsePointcutProperty

跟踪标记4.2.3.2

此方法的实现在ConfigBeanDefinitionParser类中

// 4.2.3.2 解析切入点属性
Object pointcut = parsePointcutProperty(advisorElement, parserContext);


/**
* Parses the {@code pointcut} or {@code pointcut-ref} attributes of the supplied
* {@link Element} and add a {@code pointcut} property as appropriate. Generates a
* {@link org.springframework.beans.factory.config.BeanDefinition} for the pointcut if  necessary
* and returns its bean name, otherwise returns the bean name of the referred pointcut.
*
* 解析标签上的 pointcut 属性或者 pointcut-ref 属性,并适当的添加 pointcut 属性。
* 如果必要的话生成一个 pointcut 的 BeanDefinition,并返回 bean name,否则返回引用的 pointcut 的 bean name
*/
private Object parsePointcutProperty(Element element, ParserContext parserContext) {
    
    // pointcut 和 pointcut-ref 不能同时存在
    if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
        parserContext.getReaderContext().error(
            "Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
            element, this.parseState.snapshot());
        return null;
    }
    else if (element.hasAttribute(POINTCUT)) {
        
        // Create a pointcut for the anonymous pc and register it.
        // 为匿名电脑创建一个切入点并注册
        
        // 拿到标签上的 pointcut 
        String expression = element.getAttribute(POINTCUT);
        
        // AspectJExpressionPointcut 作为 bean class 创建一个 beanDefinition
        // beanDefinition 设置同步、原型,表达式添加到属性键值对集合中
        AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
        pointcutDefinition.setSource(parserContext.extractSource(element));
        return pointcutDefinition;
    }
    else if (element.hasAttribute(POINTCUT_REF)) {
        
        // 拿到标签上的 pointcut-ref
        String pointcutRef = element.getAttribute(POINTCUT_REF);
        if (!StringUtils.hasText(pointcutRef)) {
            parserContext.getReaderContext().error(
                "'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
            return null;
        }
        
        // 直接返回字符串
        return pointcutRef;
    }
    else {
        parserContext.getReaderContext().error(
            "Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
            element, this.parseState.snapshot());
        return null;
    }
}

4.2.4 parseAspect

跟踪标记4.2.4

此方法的实现在ConfigBeanDefinitionParser类中

//4.2.4如果子标签是aspect
parseAspect(elt, parserContext);

private void parseAspect(Element aspectElement, ParserContext parserContext) {
    
    // aspect 标签上的两属性
    String aspectId = aspectElement.getAttribute(ID);
    String aspectName = aspectElement.getAttribute(REF);

    try {
        this.parseState.push(new AspectEntry(aspectId, aspectName));
        
        // 存放 BeanDefinition
        List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
        
        // 存放字符串的引用对象
        List<BeanReference> beanReferences = new ArrayList<BeanReference>();
        
        // aspect 标签中有子标签 declare-parents
        List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
        for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
            Element declareParentsElement = declareParents.get(i);
            beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
        }

        // We have to parse "advice" and all the advice kinds in one loop, to get the
        // ordering semantics right.
        // 为了得到正确顺序,我们不得不在一个循环中解析"通知"和所有通知类型
        
        // 拿到标签下的子节点
        NodeList nodeList = aspectElement.getChildNodes();
        boolean adviceFoundAlready = false;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            
            // 子节点是五种通知类型的情况下:前置、后置、最终、异常、环绕通知
            if (isAdviceNode(node, parserContext)) {
                
                // 在解析第一个通知时才进入
                if (!adviceFoundAlready) {
                    adviceFoundAlready = true;
                    if (!StringUtils.hasText(aspectName)) {
                        parserContext.getReaderContext().error(
                            "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                            aspectElement, this.parseState.snapshot());
                        return;
                    }
                    
                    // 将 aspect 标签的 ref 属性值(字符串)包装后,添加到一个集合中
                    beanReferences.add(new RuntimeBeanReference(aspectName));
                }
                
                // 4.2.4.1 解析通知
                AbstractBeanDefinition advisorDefinition = parseAdvice(
                    aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                
                // 返回的 Bean Definition 添加到另一个集合中
                beanDefinitions.add(advisorDefinition);
            }
        }

        // new 一个 AspectComponentDefinition
        AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
            aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
        
        parserContext.pushContainingComponent(aspectComponentDefinition);

        // 如果aspect标签下还有pointcut子标签,调用4.2.2方法进行解析
        List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
        for (Element pointcutElement : pointcuts) {
            parsePointcut(pointcutElement, parserContext);
        }

        // 将新创建的 AspectComponentDefinition 注册到组合组件定义器中
        parserContext.popAndRegisterContainingComponent();
    }
    finally {
        this.parseState.pop();
    }
}
4.2.4.1 parseAdvice

跟踪标记4.2.4.1

此方法的实现在ConfigBeanDefinitionParser类中

// 4.2.4.1 解析通知
AbstractBeanDefinition advisorDefinition = parseAdvice(
    aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);


/**
* Parses one of '{@code before}', '{@code after}', '{@code after-returning}',
* '{@code after-throwing}' or '{@code around}' and registers the resulting
* BeanDefinition with the supplied BeanDefinitionRegistry.
* 
* 解析五个通知标签中的一个,并且注册最终的 BeanDefinition 到工厂中
*/
private AbstractBeanDefinition parseAdvice(
    String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
    List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

    try {
        this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

        // create the method factory bean
        // 创建一个 method factory bean 的 RootBeanDefinition
        RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
        methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
        methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
        methodDefinition.setSynthetic(true);

        // create instance factory definition
        // 创建一个 instance factory definition 的 RootBeanDefinition
        RootBeanDefinition aspectFactoryDef =
            new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
        aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
        aspectFactoryDef.setSynthetic(true);

        // register the pointcut
        // 注册 pointcut ,进入这个方法
        AbstractBeanDefinition adviceDef = createAdviceDefinition(
            adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
            beanDefinitions, beanReferences);

        // configure the advisor
        // 配置顾问
        
        // 创建一个顾问的 RootBeanDefinition
        RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
        
        advisorDefinition.setSource(parserContext.extractSource(adviceElement));
        
        // 将前面得到的通知的 BeanDefinition 添加到顾问的 constructorArgumentValues 中
        advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
        
        // 是否有优先级属性在 aspect 标签上,添加进属性键值对集合中
        if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
            advisorDefinition.getPropertyValues().add(
                ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
        }

        // register the final advisor
        // 注册最终顾问的 RootBeanDefinition
        parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

        return advisorDefinition;
    }
    finally {
        this.parseState.pop();
    }
}


/**
* Creates the RootBeanDefinition for a POJO advice bean. Also causes pointcut
* parsing to occur so that the pointcut may be associate with the advice bean.
* This same pointcut is also configured as the pointcut for the enclosing
* Advisor definition using the supplied MutablePropertyValues.
*
* 创建一个 POJO 通知 bean 的 RootBeanDefinition 。这使得切入点被解析,并和通知 bean 产生了关联。
* 相同的切入点也会被封闭的顾问定义配置为切入点。顾问使用提供的 MutablePropertyValues
*/
private AbstractBeanDefinition createAdviceDefinition(
    Element adviceElement, ParserContext parserContext, String aspectName, int order,
    RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
    List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

    // 根据不同的通知类型设置 beanClass,创建 RootBeanDefinition
    RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
    adviceDefinition.setSource(parserContext.extractSource(adviceElement));

    adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
    
    // 这个 order 是子标签(通知标签)被遍历时,所处集合的位置
    adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);

    // returning 属性
    if (adviceElement.hasAttribute(RETURNING)) {
        adviceDefinition.getPropertyValues().add(
            RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
    }
    
    // throwing 属性
    if (adviceElement.hasAttribute(THROWING)) {
        adviceDefinition.getPropertyValues().add(
            THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
    }
    
    // arg-names 属性
    if (adviceElement.hasAttribute(ARG_NAMES)) {
        adviceDefinition.getPropertyValues().add(
            ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
    }

    ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
    
    // 添加上一层方法创建的 RootBeanDefinition
    cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);

    // 如果是切入点的引用,直接返回字符串,否则创建一个 RootBeanDefinition ,设置表达式等属性并返回
    Object pointcut = parsePointcutProperty(adviceElement, parserContext
                                            
    // 添加解析了切入点属性后的返回值
    if (pointcut instanceof BeanDefinition) {
        cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
        
        // Bean Definition 的情况添加到一个集合
        beanDefinitions.add((BeanDefinition) pointcut);
    }
    else if (pointcut instanceof String) {
        RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
        cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
        
        // 字符串的情况添加到另外一个集合
        beanReferences.add(pointcutRef);
    }

    // 添加上一层方法创建的 RootBeanDefinition
    cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);

    return adviceDefinition;
}

4.2.5 popAndRegisterContainingComponent

跟踪标记4.2.5

此方法的实现在ParserContext类中

//4.2.5弹出组件
parserContext.popAndRegisterContainingComponent();

public void popAndRegisterContainingComponent() {
    registerComponent(popContainingComponent());
}

public CompositeComponentDefinition popContainingComponent() {
    
    //先将ParserContext类containingComponents属性的组件弹出
    //比如aop:config,这个组件中包含着多个子标签对应的嵌套组件
    return (CompositeComponentDefinition) this.containingComponents.pop();
}

//拿到弹出的组件进行注册
public void registerComponent(ComponentDefinition component) {
    
    //再拿到parserContext中的containingComponents属性中,最后一个组件
    //但是这里前面已经弹出,这里已经没有,所以返回null
    CompositeComponentDefinition containingComponent = getContainingComponent();
    if (containingComponent != null) {
        containingComponent.addNestedComponent(component);
    }
    else {
        
        //没做具体的实现
        this.readerContext.fireComponentRegistered(component);
    }
}

到这里整个loadBeanDefinitions方法跟踪完成。

有兴趣的可以看下,spring配置文件中,组件扫描标签<context:component-scan/>的解析过程:
https://www.jianshu.com/p/e764a6959eeb

总结

4

  • 走解析自定义元素的方法。先拿到元素的名称空间URI,通过URI拿到对应的处理器,处理器中通常注册了许多解析器,每个解析器对应着该名称空间下的一个标签。
  • 4.2 利用解析器解析对应的元素标签

——————————————————————————————————

  • 4.2
  • 对于<aop:config>标签来说,其对应的解析器为 ConfigBeanDefinitionParser
  • 先创建一个组合组件定义器,标签名放入其中
  • 4.2.1 如果必要的话注册一个自动代理创建器
  • 4.2.2 然后拿到标签下的子标签,一共三种:pointcutadvisoraspect,根据不同标签走不同方法

——————————————————————————————————

  • 4.2.1
  • 工厂中已经注册了创建器,则根据其 BeanDefinition 中 beanClassName 的优先级和入参Class对象的优先级对比进行选择,入参Class对象优先级高则设置为 BeanDefinition 新的 beanClassName ;工厂中如果没有注册创建器,则创建 RootBeanDefinition,并在工厂中注册创建器。
  • 如果<aop:config>标签上有配置属性proxy-target-class或者expose-proxy等,则将其添加到 BeanDefinition 的属性键值对集合中。
  • 如果创建器是新创建的,则注册创建器到parserContext中,其实就是将创建器的 BeanDefinition 添加到组合组件定义器中

——————————————————————————————————

  • 4.2.2
  • 标签pointcut方法:创建 bean definition,指定 beanClass,默认多例,同步,将切入点表达式expression添加到bean definition的propertyValues中;将 bean definition 注册到工厂;包裹 bean definition 注册到组合组件定义器中。
  • 标签advisor方法:创建 bean definition,指定 beanClass,以adviceBeanName为key,将标签上属性advice-ref的值以 RuntimeBeanNameReference 类包装后,添加到 BeanDefinition 的属性键值对集合中;然后注册 bean definition; 解析pointcut-ref或者pointcut属性,添加到 BeanDefinition 的属性键值对集合中;包裹 bean definition 注册到组合组件定义器中。
  • 标签aspect方法

——————————————————————————————————

  • 标签aspect方法
  • 拿到aspect标签上属性 id、ref 的值,建两集合,一个放 BeanDefinition ,一个放字符串的引用对象。
  • 遍历子标签,在扫描到第一个子标签时用 RuntimeBeanReference 类包装 ref 字符串,也就是 aspectName,并将包装类放入到存放字符串引用的集合中。
  • 4.2.4.1 遍历时解析通知,将返回的 BeanDefinition 存放到另一个集合中
  • 利用 id、两集合新建一个 AspectComponentDefinition
  • 如果aspect标签下还有pointcut子标签,调用标签pointcut方法进行解析
  • 将新创建的 AspectComponentDefinition 注册到组合组件定义器中。

——————————————————————————————————

  • 4.2.4.1
  • 创建 RootBeanDefinition (methodDefinition),设置MethodLocatingFactoryBean.class为 beanClass、设置为同步、将标签aspect上的 ref 属性值和通知标签(例如:aop:before)上的 method 属性值添加到 methodDefinition 的属性键值对集合中
  • 创建 RootBeanDefinition (aspectFactoryDef),设置SimpleBeanFactoryAwareAspectInstanceFactory.class为 beanClass、设置为同步、将标签aspect上的 ref 属性值添加到 aspectFactoryDef 的属性键值对集合中
  • 创建 RootBeanDefinition (adviceDefinition),根据不同的通知类型设置 beanClass,将 ref 属性值、子标签遍历时所处的集合位置、子标签(通知标签)上的属性值添加到 adviceDefinition的属性键值对集合中;
  • 解析通知标签上 pointcut 或 pointcut-ref 属性,返回 BeanDefinition 或者字符串,根据返回值不同放入到不同的集合中;拿到 adviceDefinition 的 constructorArgumentValues 属性,依次添加 methodDefinition、BeanDefinition / 字符串、aspectFactoryDef 到 constructorArgumentValues 中;
  • 创建 RootBeanDefinition (advisorDefinition),设置AspectJPointcutAdvisor.class为 beanClass,拿到 advisorDefinition 的 constructorArgumentValues 属性,添加 adviceDefinition
  • 注册 advisorDefinition 到工厂中,并返回 advisorDefinition
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270