本文档针对spring4.2.x版本 java
Spring IOC容器初始化的过程,分为定位,载入解析以及注册,接下来本文主要分析的是spring如何去解析Bean为BeanDefinition对象的,这个只对xml声明的Bean进行分析,对于经过spring注解扫描的方式之后再作分析 node
当spring定位到spring的xml文件之后,将xml读做为文件流的形式,做为InputSource和Resource对象传递给文档解析器进行解析,文档解析的开始是XmlBeanDefinitionReader的doLoadBeanDefinitions方法。 spring
inputSource是SAX的InputSource, resource对象是对xml文件描述的一个对象 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { /** 传递inputSource对象进入doLoadDocument方法,此方法中设置了xml的版本,xml解析的模式等相关信息,而后input Source文件流读做为Document对象返回,把Document对象传递给registerBeanDefinitions方法,这个方法才是真正的把Document对象解析为BeanDefinition对象的具体实现 **/ Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch … }
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; }
先实例化出来一个BeanDefinitionDocumentReader,这个对象是Java经过本身封装工具类BeanUtils.instantiateClass 经过反射的方式实例化出来的,而后记录一下在注册以前BeanDefinition中对象的个数,接着开始去解析document,spring自己设计很是灵活,BeanDefinitionDocumentReader 的registerBeanDefinitions方法是一个抽象方法,spring自身实现一个默认的BeanDefinitionDocumentReader的一个注册器,DefaultBeanDefinitionDocumentReader,在这个子类中去实现具体的解析工做。 ide
@Override 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对象描述了spring中bean节点中定义的所全部属性和子节点 //全部的内嵌<beans>节点,都会被递归这个方法中经过递归的方式找到 //<beans>节点的默认属性都会在此方法中设置, //保存当前对象(或者父对象)BeanDefinitionParserDelegate轨迹,可能为空,可是会建立 //子类的属性描述(BeanDefinitionParserDelegate)会持有父类的一个引用 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)) { return; } } } //在xml解析的预处理,客户端能够本身定义一些本身的节点属性,用以特殊的做用,或者加强,此方法spring默认实现为空 preProcessXml(root); //把Document对象解析为BeanDefinition对象 parseBeanDefinitions(root, this.delegate); //在xml解析为BeanDefinition以后作一些后置处理,客户端能够在解析完xml以后,作一些本身的业务逻辑,目前spring的默认实现为空 postProcessXml(root); this.delegate = parent; }
接下来分析parseBeanDefinition方法 工具
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //校验是否是spring的默认命名空间,默认命名空间为http://www.springframework.org/schema/beans,若是是默认命名空间,则按照默认命名空间来解析,不然则按照客户自定义程序来解析 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; //循环xml节点,看delegate对象是不是默认命名空间,若是是则按照默认命名空间来解析节点,不然则按照客户自定义来解析 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
下面对parseDefaultElement 方法进行分析 post
此方法会查询文档中的import 标记,alias标记,以及bean和beans标记,根据这些标记进行分别匹配作相应的解析工做 this
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)) { // recurse doRegisterBeanDefinitions(ele); } }
一、 Import节点的解析 spa
首先要求import节点中必须有resource属性,而后根据resource的值判断是相对路径仍是绝对路径。 debug
而且对于resource中的占位符进行解析,例如"${user.dir}" 解析为真正的路径 设计
而后调用XmlBeanDefinitionReader的loadBeanDefinitions方法,把import节点映射为BeanDefinition对象,初始化了一个4个元素大小的LinkHashSet对象,准备好相关容器和资源,而后调用本类中的doLoadBeanDefinitions方法,作真正的映射工做 ,这是一个递归的过程,它须要把全部的import节点中的xml文件递归的找出来,而后作的均是bean节点的解析
二、 Alias节点解析
三、 Bean节点解析
使用list存储bean信息
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
/** List of bean definition names, in registration order */
private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);
/** List of names of manually registered singletons, in registration order */
private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16);
把以上信息进行存储完之后逐个转化为BeanDefinition的属性
四、 Beans节点解析
最终转化为bean节点的解析