Spring4源码分析--BeanDefinition解析与载入(AOP部分)

[TOC]

前言

回顾2017年大部分时间在做业务功能开发,在这一年里接触到了spring底层技术实现。在项目开发过程中,项目负责人会根据业务实际需求写一些通用基础组件,方便开发人员使用;spring aop在Java项目中被大量使用,在spring中使用aop有两种方式(注解、xml配置),这两种方式切入点都需要手动配置,显然不能满足通用组件便捷的特性(灵活、便捷)。

本篇文章结合基于自定义注解实现数据源动态切换基础组件实现思路讲解spring aop配置文件在spring底层如何解析为BeanDefinition,感兴趣的朋友点击查看组件介绍,源代码已上传github。

怎样才能做到便捷,AOP切入点不需要手动配置程序自动获取呢?

应用技术:自定义注解+BeanFactoryPostProcessor
思路:注解获取切入点信息(expression="execution(* com.yz.study.aop...(..))"),当然也可以绑定业务参数(比如在实现多数据源切换时标注数据库名称);通过实现BeanFactoryPostProcessor接口在spring容器bean初始化之前手动新增或修改AOP 相关类BeanDefinition定义。

基础组件使用Spring AOP改为代码实现Beandefination定义有什么好处呢?

1.基础组件使用更加灵活,在Java类或方法使用注解即可,不需要手动配置切点
2.基础组件中可继续使用Spring框架所有特性,只需在项目spring-context配置文件添加扫描配置 例如:

基于XML Spring AOP 配置示例



    
   
   
       
       
       
       
       
   

源码解析BeanDefinition

Spring aop核心概念aspect、pointcut、advisor,最终都会解析为BeanDefinition

aspect:AspectComponentDefinition
pointcut:AspectJExpressionPointcut
advisor:
before -- AspectJMethodBeforeAdvice
after -- AspectJAfterAdvice
after-returning -- AspectJAfterReturningAdvice
after-throwing -- AspectJAfterThrowingAdvice
around -- AspectJAroundAdvice
下面从spring读取xml开始到BeanDefinition解析完成完整源码示例,可跳过中间步骤,查看ConfigBeanDefinitionParser

BeanDefinitionDocumentReader

位于org.springframework.beans.factory.xml包下,实现类DefaultBeanDefinitionDocumentReader

/**
 * 解析包含Spring bean定义的XML文档
 */
public interface BeanDefinitionDocumentReader {

    /**
     * 注册BeanDefinition
     */
    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
            throws BeanDefinitionStoreException;

}

DefaultBeanDefinitionDocumentReader

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        preProcessXml(root);
        //将xml解析转换BeanDefinition
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }
    /**
     * 不同的标签使用对应的解析器
     * "import", "alias", "bean".
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                       //AOP 使用 BeanDefinitionParserDelegate代理类自定义解析器
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

BeanDefinitionParserDelegate

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

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
       //namespaceUri = http://www.springframework.org/schema/aop
        String namespaceUri = getNamespaceURI(ele);
        //handler = AopNamespaceHandler
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

AopNamespaceHandler

AopNamespaceHandler继承NamespaceHandlerSupport并重写了init方法

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    @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());
    }

}

NamespaceHandlerSupport

具体解析方法在ConfigBeanDefinitionParser中

public BeanDefinition parse(Element element, ParserContext parserContext) {
        return findParserForElement(element, parserContext).parse(element, parserContext);
    }

    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
       //localName = config
        String localName = parserContext.getDelegate().getLocalName(element);
        // parser = ConfigBeanDefinitionParser
        BeanDefinitionParser parser = this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        return parser;
    }

ConfigBeanDefinitionParser

在parse方法中终于看到了AOP的核心pointcut、advisor、aspect

public BeanDefinition parse(Element element, ParserContext parserContext) {
        CompositeComponentDefinition compositeDef =
                new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
        parserContext.pushContainingComponent(compositeDef);

        configureAutoProxyCreator(parserContext, element);

        List childElts = DomUtils.getChildElements(element);
        for (Element elt: childElts) {
            String localName = parserContext.getDelegate().getLocalName(elt);
            if (POINTCUT.equals(localName)) {
                 //切入点解析
                parsePointcut(elt, parserContext);
            }
            else if (ADVISOR.equals(localName)) {
                 //增强器解析
                parseAdvisor(elt, parserContext);
            }
            else if (ASPECT.equals(localName)) {
                 //切面解析
                parseAspect(elt, parserContext);
            }
        }

        parserContext.popAndRegisterContainingComponent();
        return null;
    }
    
    //切入点解析方法
    private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
        String id = pointcutElement.getAttribute(ID);
        String expression = pointcutElement.getAttribute(EXPRESSION);

        AbstractBeanDefinition pointcutDefinition = null;

        try {
            this.parseState.push(new PointcutEntry(id));
            pointcutDefinition = createPointcutDefinition(expression);
            pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

            String pointcutBeanName = id;
            if (StringUtils.hasText(pointcutBeanName)) {
                parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
            }
            else {
                pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
            }

            parserContext.registerComponent(
                    new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
        }
        finally {
            this.parseState.pop();
        }

        return pointcutDefinition;
    }
//增强器解析方法
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
        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 {
                advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
            }

            Object pointcut = parsePointcutProperty(advisorElement, parserContext);
            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();
        }
    }
    //切面解析方法
    private void parseAspect(Element aspectElement, ParserContext parserContext) {
        String aspectId = aspectElement.getAttribute(ID);
        String aspectName = aspectElement.getAttribute(REF);

        try {
            this.parseState.push(new AspectEntry(aspectId, aspectName));
            List beanDefinitions = new ArrayList();
            List beanReferences = new ArrayList();

            List 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(
                                    " tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                                    aspectElement, this.parseState.snapshot());
                            return;
                        }
                        beanReferences.add(new RuntimeBeanReference(aspectName));
                    }
                    AbstractBeanDefinition advisorDefinition = parseAdvice(
                            aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                    beanDefinitions.add(advisorDefinition);
                }
            }

            AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
                    aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
            parserContext.pushContainingComponent(aspectComponentDefinition);

            List pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
            for (Element pointcutElement : pointcuts) {
                parsePointcut(pointcutElement, parserContext);
            }

            parserContext.popAndRegisterContainingComponent();
        }
        finally {
            this.parseState.pop();
        }
    }

以上属于原创文章,转载请注明作者@怪咖
QQ:208275451

你可能感兴趣的:(Spring4源码分析--BeanDefinition解析与载入(AOP部分))