SpringIOC(三) Bean加载进容器

《Spring揭秘》学习笔记

Posted by Jesse on December 22, 2018

[TOC]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ClassPathResource resource = new ClassPathResource("bean.xml"); 

//根据 Xml 配置文件创建 Resource 资源对象。ClassPathResource 是 Resource 接口的子类,bean.xml 文件中的内容是我们定义的 Bean 信息。 

DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 

//创建一个 BeanFactory。DefaultListableBeanFactory 是 BeanFactory 的一个子类,BeanFactory 作为一个接口,其实它本身是不具有独立使用的功能的,而 DefaultListableBeanFactory 则是真正可以独立使用的 IOC 容器,它是整个 Spring IOC 的始祖 

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 

//XmlBeanDefinitionReader 读取器,用于载入 BeanDefinition 。 

reader.loadBeanDefinitions(resource); 

//开启 Bean 的载入和注册进程,完成后的 Bean 放置在 IOC 容器中。 

  • 获取资源

  • 获取 BeanFactory

  • 根据新建的 BeanFactory 创建一个BeanDefinitionReader对象,该Reader 对象为资源的解析器

  • 装载资源 整个过程就分为三个步骤:资源定位、装载、注册

资源定位。我们一般用外部资源来描述 Bean 对象,所以在初始化 IOC 容器的第一步就是需要定位这个外部资源。

装载。就是将用户定义的 Bean 表示成 IOC 容器的内部数据结构:BeanDefinition。在 IOC 容器内部维护着一个 BeanDefinition Map 的数据结构

注册。向IOC容器注册在第二步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistry 接口来实现的。在 IOC 容器内部其实是将第二个过程解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IOC 容器就是通过这个 HashMap 来维护这些 BeanDefinition 的。这个过程并没有完成依赖注入,依赖注册是发生在应用第一次调用 getBean() 向容器索要 Bean 时。

BeanDefinition 的载入和解析

XmlBeanDefinitionReader是BeanDefinitionReader的实现类

其方法

1
2
3
4
5
6
7
8
9
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { 

//将读入的XML资源进行特殊编码处理 

return loadBeanDefinitions(new EncodedResource(resource)); 

} 

才是加载资源的真正实现。

在这个方法会将资源 resource 包装成一个 EncodedResource 实例对象,这是为了对 Resource 进行编码,保证内容读取的正确性。然后调用 loadBeanDefinitions() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 

// 省略一些代码 

try { 
// 将资源文件转为 InputStream 的 IO 流 
InputStream inputStream = encodedResource.getResource().getInputStream(); 

try { 
// 从 InputStream 中得到 XML 的解析源 
InputSource inputSource = new InputSource(inputStream); 

if (encodedResource.getEncoding() != null) { 

inputSource.setEncoding(encodedResource.getEncoding()); 

} 

// 具体的读取过程 

return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 

} 

finally { 

inputStream.close(); 

} 

} 

// 省略一些代码 

} 

从 encodedResource 源中获取 xml 的解析源,调用 doLoadBeanDefinitions() 执行具体的解析过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 

throws BeanDefinitionStoreException { 

try { 

Document doc = doLoadDocument(inputSource, resource); 

return registerBeanDefinitions(doc, resource); 

} 

// 省略很多catch代码 

转换为 Document 对象

调用 doLoadDocument() 会将 Bean 定义的资源转换为 Document 对象。

1
2
3
4
5
6
7
8
9
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { 

return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, 

getValidationModeForResource(resource), isNamespaceAware()); 

} 

loadDocument() 方法接受五个参数:

  • inputSource:加载 Document 的 Resource 源

  • entityResolver:解析文件的解析器

  • errorHandler:处理加载 Document 对象的过程的错误

  • validationMode:验证模式

  • namespaceAware:命名空间支持。如果要提供对 XML 名称空间的支持,则为true

loadDocument() 在类 DefaultDocumentLoader 中提供了实现,如下:

1
2
3
4
5
6
7
8
9
10
11
12
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
      // 创建文件解析工厂
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        // 创建文档解析器
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        // 解析 Spring 的 Bean 定义资源
        return builder.parse(inputSource);
    }

这到这里,就已经将定义的 Bean 资源文件,载入并转换为 Document 对象了,那么下一步就是如何将其解析为 Spring IOC 管理的 Bean 对象并将其注册到容器中。这个过程有方法 registerBeanDefinitions() 实现。如下:

1
2
3
4
5
6
7
8
9
10
11
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
       // 创建 BeanDefinitionDocumentReader 来对 xml 格式的BeanDefinition 解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 获得容器中注册的Bean数量
        int countBefore = getRegistry().getBeanDefinitionCount();

        // 解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
        // 具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

首先创建 BeanDefinition 的解析器 BeanDefinitionDocumentReader,然后调用 documentReader.registerBeanDefinitions() 开启解析过程,这里使用的是委派模式,具体的实现由子类 DefaultBeanDefinitionDocumentReader 完成。

1
2
3
4
5
6
7
8
9
10
11
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
       // 获得XML描述符
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");

        // 获得Document的根元素
        Element root = doc.getDocumentElement();

        // 解析根元素
        doRegisterBeanDefinitions(root);
    }

对 Document 对象的解析

从 Document 对象中获取根元素 root,然后调用 doRegisterBeanDefinitions() 开启真正的解析过程。

1
2
3
4
5
6
7
8
9
10
11
12
protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

       // 省略部分代码

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

preProcessXml()postProcessXml()前置、后置增强处理,目前 Spring 中都是空实现, parseBeanDefinitions() 是对根元素 root 的解析注册过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
       // Bean定义的Document对象使用了Spring默认的XML命名空间
        if (delegate.isDefaultNamespace(root)) {
          // 获取Bean定义的Document对象根元素的所有子节点
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                // 获得Document节点是XML元素节点
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    // Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
                    if (delegate.isDefaultNamespace(ele)) {
                       // 使用Spring的Bean规则解析元素节点(默认解析规则)
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                       // 没有使用Spring默认的XML命名空间,则使用用户自定义的解析规则解析元素节点
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
          // Document 的根节点没有使用Spring默认的命名空间,则使用用户自定义的解析规则解析
            delegate.parseCustomElement(root);
        }
    }

迭代 root 元素的所有子节点,对其进行判断,若节点为默认命名空间,则ID调用 parseDefaultElement() 开启默认标签的解析注册过程,否则调用 parseCustomElement() 开启自定义标签的解析注册过程。

标签解析

若定义的元素节点使用的是 Spring 默认命名空间,则调用 parseDefaultElement() 进行默认标签解析,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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>元素,则进行Bean解析注册
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }

        // // 如果元素节点<Beans>元素,则进行Beans解析
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

对四大标签:import、alias、bean、beans 进行解析,其中 bean 标签的解析为核心工作。

对于默认标签则由 parseCustomElement() 负责解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }

    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        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));
    }

获取节点的 namespaceUri,然后根据该 namespaceuri 获取相对应的 Handler,调用 Handler 的 parse() 方法即完成自定义标签的解析和注入。

注册 BeanDefinition

经过上面的解析,则将 Document 对象里面的 Bean 标签解析成了一个个的 BeanDefinition ,下一步则是将这些 BeanDefinition 注册到 IOC 容器中。动作的触发是在解析 Bean 标签完成后,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        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));
        }
    }

调用 BeanDefinitionReaderUtils.registerBeanDefinition() 注册,其实这里面也是调用 BeanDefinitionRegistry 的 registerBeanDefinition()来注册 BeanDefinition ,不过最终的实现是在 DefaultListableBeanFactory 中实现,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

      // 省略一堆校验

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
          // 省略一堆 if
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    //放入容器中
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

这段代码最核心的部分是这句 this.beanDefinitionMap.put(beanName, beanDefinition) ,所以注册过程也不是那么的高大上,就是利用一个 Map 的集合对象来存放,key 是 beanName,value 是 BeanDefinition。

至此,整个 IOC 的初始化过程就已经完成了,从 Bean 资源的定位,转换为 Document 对象,接着对其进行解析,最后注册到 IOC 容器中,都已经完美地完成了。现在 IOC 容器中已经建立了整个 Bean 的配置信息,这些 Bean 可以被检索、使用、维护,他们是控制反转的基础,是后面注入 Bean 的依赖。最后用一张流程图来结束这篇总结之文。