07--SpringIoC容器初始化BeanDefinition加载

前两篇分析了Spring IoC容器初始化资源文件定位,接下来进入到第二步资源文件解析,该过程就是把用户所定义的bean转换为IoC容器的内部表示,这里又涉及到几个非常重要的接口和类:

  • BeanDefinition–>bean的容器内部表示,例如各种bean标签通过解析之后都可以转换为BeanDefinition
  • BeanDefinitionReader–>通过给定的Resource对象将资源文件解析为BeanDefinition
  • BeanDefinitionParserDelegate–>解析资源文件的委托对象,BeanDefinitionReader并不会亲自解析资源文件,而是委托给BeanDefinitionParserDelegate解析
  • BeanDefinitionRegistry–>维护解析到的BeanDefinition,相当于spring的内部一个数据库,通过hashMap来维护注册的BeanDefinition对象

该过程大致分为三步

  • 将加载到的资源文件转换为Document对象
  • 将资源文件解析委托BeanDefinitionParserDelegate对象进行解析
  • 解析默认标签或解析自定义标签
1.资源文件转换为Document对象
public class XmlBeanFactory extends DefaultListableBeanFactory {

    //实例化XmlBeanDefinitionReader对象
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


    /**
     * 通过指定Resource对象创建XmlBeanFactory实例
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    /**
     * 通过指定Resource对象和父BeanFactory创建XmlBeanFactory实例
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource          the XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        //解析xml配置文件,将其转换为IoC容器的内部表示
        this.reader.loadBeanDefinitions(resource);
    }

}

this.reader.loadBeanDefinitions(resource);为入口,开始分析
XmlBeanDefinitionReader–>

//加载BeanDefinition
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
        //将资源文件转换为Document对象
        Document doc = doLoadDocument(inputSource, resource);
        //加载BeanDefinition
        return registerBeanDefinitions(doc, resource);
    }
   //省略catch代码....
}

Document doc = doLoadDocument(inputSource, resource);的功能就是讲资源文件转换为Document,该过程与普通的xml文件解析原理大致相同,不再进行详细的分析

2.将资源文件解析委托BeanDefinitionParserDelegate对象进行解析

进入到registerBeanDefinitions(doc, resource)方法继续看…

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //创建BeanDefinitionDocumentReader对象
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    //获取BeanDefinitionRegistry中已经加载到的bean对象个数
    int countBefore = getRegistry().getBeanDefinitionCount();
    //注册bean
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    //返回新注册的bean的个数
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

通过这段代码,可以分析到真正的注册过程,应该在documentReader.registerBeanDefinitions(doc, createReaderContext(resource));中,继续跟踪代码…

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) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    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);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    preProcessXml(root);
    //解析资源文件
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}

再进去到parseBeanDefinitions(root, this.delegate);方法中

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    //spring默认命名空间
    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);
    }
}

代码跟踪到这里,终于可以看到spring对资源文件的解析了,解析的种类分为两种

  • spring默认标签
  • 自定义标签
2.1 spring默认标签解析
//默认标签解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    //import标签
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    //alias标签
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    //bean标签
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    //beans标签
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

默认标签解析也分为四种类型:

  • import标签
  • alias标签
  • bean标签
  • beans标签

这里边最常见也最重要的是bean标签,这里我们只分析这一种标签

2.2 spring bean标签解析
/**
 * 解析bean标签将其转换为definition并注册到BeanDefinitionRegistry
 * Process the given bean element, parsing the bean definition and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //解析Element节点,并将转换信息封装到BeanDefinitionHolder
    //BeanDefinitionHolder封装了definition,beanName,以及aliases信息
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // 执行注册
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        // 发送注册事件
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);的作用就是解析Element节点,并将转换信息封装到BeanDefinitionHolder,BeanDefinitionHolder封装了definition,beanName,以及aliases信息,接下来的代码就是对bean标签的各种解析,先来看下BeanDefinitionParserDelegate的部分源码

public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
public static final String TRUE_VALUE = "true";
public static final String FALSE_VALUE = "false";
public static final String DEFAULT_VALUE = "default";
public static final String DESCRIPTION_ELEMENT = "description";
public static final String AUTOWIRE_NO_VALUE = "no";
public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
public static final String NAME_ATTRIBUTE = "name";
public static final String BEAN_ELEMENT = "bean";
public static final String META_ELEMENT = "meta";
public static final String ID_ATTRIBUTE = "id";
public static final String PARENT_ATTRIBUTE = "parent";
public static final String CLASS_ATTRIBUTE = "class";
public static final String ABSTRACT_ATTRIBUTE = "abstract";
public static final String SCOPE_ATTRIBUTE = "scope";
private static final String SINGLETON_ATTRIBUTE = "singleton";
public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
public static final String AUTOWIRE_ATTRIBUTE = "autowire";
public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
public static final String PRIMARY_ATTRIBUTE = "primary";
public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
public static final String INDEX_ATTRIBUTE = "index";
public static final String TYPE_ATTRIBUTE = "type";
public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
public static final String KEY_TYPE_ATTRIBUTE = "key-type";
public static final String PROPERTY_ELEMENT = "property";
public static final String REF_ATTRIBUTE = "ref";
public static final String VALUE_ATTRIBUTE = "value";
public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
public static final String REPLACER_ATTRIBUTE = "replacer";
public static final String ARG_TYPE_ELEMENT = "arg-type";
public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
public static final String REF_ELEMENT = "ref";
public static final String IDREF_ELEMENT = "idref";
public static final String BEAN_REF_ATTRIBUTE = "bean";
public static final String PARENT_REF_ATTRIBUTE = "parent";
public static final String VALUE_ELEMENT = "value";
public static final String NULL_ELEMENT = "null";
public static final String ARRAY_ELEMENT = "array";
public static final String LIST_ELEMENT = "list";
public static final String SET_ELEMENT = "set";
public static final String MAP_ELEMENT = "map";
public static final String ENTRY_ELEMENT = "entry";
public static final String KEY_ELEMENT = "key";
public static final String KEY_ATTRIBUTE = "key";
public static final String KEY_REF_ATTRIBUTE = "key-ref";
public static final String VALUE_REF_ATTRIBUTE = "value-ref";
public static final String PROPS_ELEMENT = "props";
public static final String PROP_ELEMENT = "prop";
public static final String MERGE_ATTRIBUTE = "merge";
public static final String QUALIFIER_ELEMENT = "qualifier";
public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";

从这些常量的定义可以发现,spring默认标签里的信息,都在这里有定义,接下来就是通过解析Element节点,把节点中的信息封装至BeanDefinition对象,再将BeanDefinition对象封装至BeanDefinitionHolder返回,然后循环所有节点并解析节点….
解析的代码比较简单,但是对应的标签太多,这里就不粘贴了,大家可以跟踪调试代码阅读
对BeanDefinition的加载就讲到这里了….

猜你喜欢

转载自blog.csdn.net/lyc_liyanchao/article/details/82384794