本文最后更新于:2022年6月11日 下午
装载BeanDefinition总结
该篇是对前边十几篇的总结。
IOC容器初始化的过程:Resource定位 、BeanDefinition的装载和解析 、BeanDefinition注册 。
Resource定位 :一般使用外部资源描述bean
对象,所以初始化IOC容器第一步需要定位这个外部资源(统一资源加载策略)。
BeanDefinition的装载和解析 :装载就是BeanDefinition的载入,通过BeanDefinitionReader读取、解析Resource资源,也就是将用户定义的Bean表示成IOC容器内部的数据结构–BeanDefinition。
注:1.IOC容器内部维护着一个BeanDefinitionMap的数据结构。
2.配置文件中的每一个都对应着一个BeanDefinition对象。
BeanDefinition注册 :向IOC容器内部维护的BeanDefinitionMap中注册第二步解析好的BeanDefinition对象,该过程通过BeanDefinitionRegistry接口实现。
注:1.该过程并没有完成依赖注入(Bean创建),Bean创建发生在第一次调用getBean()
方法,向容器索取 Bean时,因为默认是懒加载。
2.可以修改加载方式,即设置某个Bean的lazyinit=false
,那么这个Bean的依赖注入会在IOC容器初始化的时候完成。
在初始阶段,做过一个实例:
1 2 3 4 5 6 7 8 ClassPathResource resource = new ClassPathResource ("bean.xml" );DefaultListableBeanFactory factory = new DefaultListableBeanFactory ();XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (factory); reader.loadBeanDefinitions(resource);
① :根据 Xml 配置文件创建 Resource 资源对象。ClassPathResource 是 Resource 接口的子类,bean.xml 文件中的内容是我们定义的 Bean 信息。
② :创建一个 BeanFactory 。DefaultListableBeanFactory 是 BeanFactory 的一个子类,BeanFactory 作为一个接口,其实它本身是不具有独立使用的功能的,而 DefaultListableBeanFactory 则是真正可以独立使用的 IoC 容器,它是整个 Spring IoC 的始祖。
③ :创建 XmlBeanDefinitionReader 读取器,用于载入和解析 BeanDefinition 。
④ :开始 BeanDefinition 的载入和注册进程,完成后的 BeanDefinition 放置在 IoC 容器中。
Resource
定位Spring为了解决资源定位问题,提供了两个接口:Resource 、ResourceLoader 。
Resource:Spring 统一资源的抽象接口。
ResourceLoader:Spring 资源加载的统一抽象。
Resource 资源的定位需要 Resource 和 ResourceLoader 两个接口互相配合,在上面那段代码中 new ClassPathResource("bean.xml")
为我们定义了Resource,那么 ResourceLoader 则是在什么时候初始化的呢?看 XmlBeanDefinitionReader 构造方法:
1 2 3 4 5 6 public XmlBeanDefinitionReader (BeanDefinitionRegistry registry) { super (registry); }
直接调用父类 AbstractBeanDefinitionReader 构造方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 protected AbstractBeanDefinitionReader (BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null" ); this .registry = registry; if (this .registry instanceof ResourceLoader) { this .resourceLoader = (ResourceLoader) this .registry; } else { this .resourceLoader = new PathMatchingResourcePatternResolver (); } if (this .registry instanceof EnvironmentCapable) { this .environment = ((EnvironmentCapable) this .registry).getEnvironment(); } else { this .environment = new StandardEnvironment (); } }
BeanDefinition
的装载和解析第④步reader.loadBeanDefinitions(resource);
开始BeanDefinition的解析过程:
1 2 3 4 5 @Override public int loadBeanDefinitions (Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource (resource)); }
在这个方法会将资源 Resource 包装成一个 EncodedResource
实例对象,然后调用 loadBeanDefinitions(EncodedResource encodedResource)
方法。而将 Resource 封装成 EncodedResource 主要是为了对 Resource 进行编码 ,保证内容读取的正确性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int loadBeanDefinitions (EncodedResource encodedResource) throws BeanDefinitionStoreException { try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource (inputStream); if (encodedResource.getEncoding() != null ) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } }
从 encodedResource
源中获取 xml 的解析源,然后调用 doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,执行具体的解析过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 protected int doLoadBeanDefinitions (InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); return count; } }
在该方法中主要做两件事:
1、调用doLoadDocument(inputSource, resource)
根据 xml 解析源获取相应的 Document 对象。
2、调用 registerBeanDefinitions(Document doc, Resource resource)
方法,开启 BeanDefinition 的解析注册过程。
获取相应的Document
对象 调用doLoadDocument(inputSource, resource)
根据 xml 解析源获取相应的 Document 对象。
1 2 3 protected Document doLoadDocument (InputSource inputSource, Resource resource) throws Exception { return this .documentLoader.loadDocument(inputSource, getEntityResolver(), this .errorHandler,getValidationModeForResource(resource), isNamespaceAware()); }
五个参数:
1、InputSource:加载 Document 的 Resource 源。
2、EntityResolver:解析文件的解析器,重点,前边已经分析
3、ErrorHandler:处理加载 Document 对象的过程的错误。
4、validationMode:验证模式,重点,前边已经分析,默认自动获取,值为VALIDATION_AUTO,1。
5、namespaceAware:命名空间支持。如果要提供对 XML 名称空间的支持,则为 true
,默认false。
loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
方法,在类 DefaultDocumentLoader 中提供了实现。
1 2 3 4 5 6 7 8 9 10 11 @Override public Document loadDocument (InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }
解析注册BeanDefinition
信息 根据上个步骤转换的Document实例,解析注册 BeanDefinition 信息,该过程调用registerBeanDefinitions(doc, resource);
实现:
1 2 3 4 5 6 7 8 9 10 11 12 public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
1、首先,创建 BeanDefinition 的解析器 BeanDefinitionDocumentReader 。 2、然后,调用该 BeanDefinitionDocumentReader 的 registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
方法,开启解析过程,这里使用的是委派模式 ,具体的实现由子类 DefaultBeanDefinitionDocumentReader 完成。
1 2 3 4 5 6 7 8 @Override public void registerBeanDefinitions (Document doc, XmlReaderContext readerContext) { this .readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); }
对Document
对象进行解析 从 Document 对象中获取根元素 root ,然后调用 ``doRegisterBeanDefinitions(Element root)` 方法,开启真正的解析过程。
1 2 3 4 5 6 7 8 9 10 11 12 protected void doRegisterBeanDefinitions (Element root) { this .delegate = createDelegate(getReaderContext(), root, parent); preProcessXml(root); parseBeanDefinitions(root, this .delegate); postProcessXml(root); }
注:preProcessXml(Element root)
、#postProcessXml(Element root)
为前置、后置增强处理,目前 Spring 中都是空实现。
parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
是对根元素 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 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); } }
迭代 root 元素的所有子节点,对其进行判断:
1、若节点为默认命名空间,则调用 parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
方法,开启默认标签的解析注册过程。
2、否则,调用 BeanDefinitionParserDelegate.parseCustomElement(Element ele)
方法,开启自定义标签的解析注册过程。
两个过程前边都有介绍。
对默认标签的解析
1 2 3 4 5 6 7 8 9 10 11 12 13 private void parseDefaultElement (Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { doRegisterBeanDefinitions(ele); } }
对四大标签:<import>
、<alias>
、<bean>
、<beans>
进行解析。其中 <bean>
标签的解析为核心工作,解析为BeanDefinition 。
对自定义标签的解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Nullable public BeanDefinition parseCustomElement (Element ele) { return parseCustomElement(ele, null ); }@Nullable 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
获取相对应的 NamespaceHandler,最后调用 NamespaceHandler 的 parse(Element element, ParserContext parserContext)
方法,即完成自定义标签的解析和注入。
注册BeanDefinition
将 Document 对象里面的 Bean 标签解析成了一个个的 BeanDefinition 后,下一步则是将这些 BeanDefinition 注册到 IoC 容器中。动作的触发是在解析 Bean 标签完成后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected void processBeanDefinition (Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null ) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'" , ele, ex); } getReaderContext().fireComponentRegistered(new BeanComponentDefinition (bdHolder)); } }
调用 BeanDefinitionReaderUtils.registerBeanDefinition()
方法来注册。其实,这里面也是调用 BeanDefinitionRegistry 的 .registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
方法,来注册 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 @Override public void registerBeanDefinition (String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { BeanDefinition existingDefinition = this .beanDefinitionMap.get(beanName); if (existingDefinition != null ) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException (beanName, beanDefinition, existingDefinition); } else { } this .beanDefinitionMap.put(beanName, beanDefinition); } else { this .beanDefinitionMap.put(beanName, beanDefinition); } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
总结 现在 IoC 容器中已经建立了整个 Bean 的配置信息,这些 Bean 可以被检索、使用、维护,他们是控制反转的基础,是后面注入 Bean 的依赖。
流程图: