欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring源码解析之默认标签的解析与Bean的注册

程序员文章站 2022-04-30 10:00:17
...

前面我们介绍了Spring源码解析之容器的创建与XML配置的读取Spring IOC容器的创建也就是DefaultListableBeanFactory的创建,配置文件的加载最终被解析为Document实例,本篇我们介绍配置标签的解析与Bean的注册,在前面一篇我们最终看到标签的解析与Bean的注册是在XmlBeanDefinitionReader的registerBeanDefinitions(doc, resource)方法中完成的,下面我们查看该方法的源码:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //标签的解析最终是通过BeanDefinitionDocumentReader 解析的,本行代码创建了一个  BeanDefinitionDocumentReader 实例
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    //调用registerBeanDefinitions方法解析
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

从上面的代码中可以看到标签的解析最终交给BeanDefinitionDocumentReader实例完成的,这里我们重点关注的是createReaderContext(resource),它返回了一个XmlReaderContext实例。因为该方法会传入我们的DefaultListableBeanFactory实例。代码如下:

public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,this.sourceExtractor, this, getNamespaceHandlerResolver());
}

如上方法所示,createReaderContext(Resource resource)传入了一个this对象,即XmlBeanDefinitionReader实例,明明传入的是XmlBeanDefinitionReader为何这里会说传入一个DefaultListableBeanFactory实例呢?在前面讲解加载配置的时候,我们使用XmlBeanDefinitionReader的构造方法传入了一个DefaultListableBeanFactory实例,因此,后续会使用createReaderContext(Resource resource)创建的XmlReaderContext实例获取我们的IOC容器,即DefaultListableBeanFactory实例。下面我们接着砍BeanDefinitionDocumentReader是如何解析配置的。

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

上面的代码比较简单,获取配置的根元素,然后解析根元素,我们继续看doRegisterBeanDefinitions(Element root)方法,元素的解析最终是委派给BeanDefinitionParserDelegate 实例完成的。

protected void doRegisterBeanDefinitions(Element root){
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    .....//这部分代码省略,判断是否是profile指定的配置,如果不是则返回
    preProcessXml(root);
    //解析配置
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
    this.delegate = parent;
}

如下代码,是createDelegate(getReaderContext(), root, parent)创建BeanDefinitionParserDelegate 实例的逻辑:

protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
    //创建BeanDefinitionParserDelegate 实例
    BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
    //做一些初始化工作
    delegate.initDefaults(root, parentDelegate);
    return delegate;
}

源码分析道这里,后续就是使用BeanDefinitionParserDelegate 解析标签注册Bean了,我们继续看方法parseBeanDefinitions(root, this.delegate);这部分代码比较简单,判断标签是否是默认标签还是自定义标签,根据Node的getNamespaceURI()是否为http://www.springframework.org/schema/beans或者为null来判断是否为默认空间。

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 {
                            //解析自定义标签
			    delegate.parseCustomElement(ele);
			}
		    }
		}
	} else {
    //否则解析自定义标签
    delegate.parseCustomElement(root);
    }
}

上面的方法根据节点的命名空间是否是默认的命名空间分别使用不同的方法解析标签,本篇我们主要介绍解析默认标签,也就是parseDefaultElement(ele, delegate)方法,源码如下所示:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
       //import标签
        importBeanDefinitionResource(ele);
    } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
       //alias标签
        processAliasRegistration(ele);
    } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        //Bean标签
        processBeanDefinition(ele, delegate);
    } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // beans标签
        doRegisterBeanDefinitions(ele);
    }
}

import标签用于解析导入的资源最终封装为Resource实例,alias则是为Bean注册别名,最终会被存储为SimpleAliasRegistry的Map变量中,DefaultListableBeanFactory继承了SimpleAliasRegistry类,我们要重点讲解的是bean标签的解析,也就是processBeanDefinition方法:代码如下:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //这里会解析bean标签并且将Bean标签包装为BeanDefinitionHolder 实例
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            //注册Bean
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
	} ......//异常以及其他逻辑
    }
}

上面的方法中,一共两部分,一是解析元素最终封装为BeanDefinitionHolder,而将Bean注册容器,我们先看是如何将Bean注册到容器的,然后再看标签的解析。注册Bean是使用的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry())一行代码,我们查看源码:

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {
    //获取Bean的名称
    String beanName = definitionHolder.getBeanName();
    //注册Bean
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    //为bean 注册别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

上面代码中通过调用BeanDefinitionRegistry registerBeanDefinition(...)方法注册Bean,BeanDefinitionRegistry 也是DefaultListableBeanFactory的接口,如下为BeanDefinitionRegistry 的实现:

Spring源码解析之默认标签的解析与Bean的注册

 上面只是部分实现,其中DefaultListableBeanFactoryGenericApplicationContextSimpleBeanDefinitionRegistry都实现了registerBeanDefinition(...)方法,SimpleBeanDefinitionRegistry只是一个简单的容器,什么都没有做,里面有一个map用于存储Bean名称和Bean的关系,GenericApplicationContext最终调用的还是DefaultListableBeanFactory的方法,因此我们只需要关注DefaultListableBeanFactory即可,代码如下:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
	}......//catch代码
    //声明一个BeanDefiinition实例
    BeanDefinition oldBeanDefinition;
    //从缓存中取出一个BeanDefinition实例
    oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    if (oldBeanDefinition != null) {
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
    }
    //缓存BeanDefinition
    this.beanDefinitionMap.put(beanName, beanDefinition);
    } else {
        //如果BeanDefinition没有找到,但是已经有Bean创建//即容器已经启动
        if (hasBeanCreationStarted()) {
            synchronized (this.beanDefinitionMap) {
                  //缓存实例
                this.beanDefinitionMap.put(beanName, beanDefinition);
		List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
		updatedDefinitions.addAll(this.beanDefinitionNames);
		updatedDefinitions.add(beanName);
		this.beanDefinitionNames = updatedDefinitions;
	if (this.manualSingletonNames.contains(beanName)) {
	    Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
	    updatedSingletons.remove(beanName);
	    this.manualSingletonNames = updatedSingletons;
	    }
	}
    } else {
    //容器没有启动。缓存Bean
    this.beanDefinitionMap.put(beanName, beanDefinition);
    //添加BeanName
    this.beanDefinitionNames.add(beanName);
    this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }
    if (oldBeanDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

觉得上面的代码非常长,但是主要逻辑就是将BeanDefinition存储到DefaultListableBeanFactoryMap字段中,如下为其声明:

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

 经过上面的代码,我们知道Bean注册的最终形式为BeanDefinition,它存储在DefaultListableBeanFactory的Map实例中,也就是说我们的XML配置的Bean或者注解的Bean最终的形式为BeanDefinition,我们先看BeanDefinition的层次结构图,然后再看标签是如何解析为BeanDefinition的。

Spring源码解析之默认标签的解析与Bean的注册

如上为BeanDefinition的所有实现,下面我们查看标签是如何解析BeanDefinition的,在前面的BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),通过BeanDefinitionParserDelegate 解析标签,我们查看最终的解析部分的源码

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    //获取Bean标签的id或者name属性
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    List<String> aliases = new ArrayList<String>();
    //获取Bean的别名
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr =  StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }
    String beanName = id;
    //如果id属性为空,并且name属性不为空,从name属性的值中取出第一个当作Bean名称
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        beanName = aliases.remove(0);
    }
    //如果containingBean ==null,检测Name的唯一性
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }
    //将标签解析为BeanDefinition实例
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    //如果beanDefinition 不为空封装为BeanDefinitionHolder实例
    if (beanDefinition != null) {
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;
}

上面的方法中最终调用了 parseBeanDefinitionElement(ele, beanName, containingBean);解析Bean标签代码如下:

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
    this.parseState.push(new BeanEntry(beanName));
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    try {
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }
        //创建BeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        //解析Bean标签的其他属性,比如scope,autowire等
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        ......//解析Bean标签的其他内容meta  properties,constructor等
        return bd;
    }......//catch代码
    return null
}

上面的代码中通过createBeanDefinition(className, parent)创建BeanDefinition实例,最终调用了如下的方法,使用BeanDefinition的实现类GenericBeanDefinition创建了一个实例,并且为其设置名称和Class名称

public static AbstractBeanDefinition createBeanDefinition(String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
    GenericBeanDefinition bd = new GenericBeanDefinition();
    bd.setParentName(parentName);
    if (className != null) {
        if (classLoader != null) {
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
        }else {
            bd.setBeanClassName(className);
        }
    }
    return bd;
}

到此我们Bean的注册的源码分析完了,综上所述:首先Bean最终被解析为BeanDefinition实例并且存储在DefaultListableBeanFactoryMap实例中。其解析被委托BeanDefinitionParserDelegate 完成的。Bean的解析过程中,并没有为Bean创建实例。Bean实例的创建时在获取的时候创建的。