spring 源码分析(1)-xml文件解析
我們在最開始接觸spring的時候,看到不少書spring入門的例子如下
ApplicationContext atx = new ClassPathXmlApplicationContext("application.xml"); atx.getBean("benefitService");上面這個例子第一行是表示如何初始化一個spring 容器,第二表示如何從一個已經初始化后的spring容器中按bean id得到這個bean, 絕大部分spring應用中,bean都是按業務模板和層次分別配置在不同的xml文件中, spring容器根據配置的xml文件名路徑去分別解析這些xml 配置文件,生成相應的BeanDefinition 實例,一個bean對應一個BeanDefinition, 解析完成bean 的xml配置文件之后,spring容器就開始初始bean,大概的過程如下:
這篇文章主要分析第一個階段,即xml配置文件 ---->BeanDefinition這個過程,首先根據IDE工具看一下ClassPathXmlApplicationContext 這個類的繼承關系:
通過這個繼續關系,發現ClassPathXmlApplicationContext也是間接實現了ResourceLoader這個接口, ResourceLoader的實現類主要用于根據給定的資源文件地址返回對應的Resource,在本例中,這個資源文件就是application.xml;
接著往下看
代碼到了含有三個參數的構造方法,主要有三個步驟
super(parent)
這個步驟主要是調父類的構造器初始化容器的parent對象,這示例中,parent這個參數為空,其次是初始化資源模式解析器resourcePatternResolver,是一個實現了ResourceLoader的類,源碼如下 :
public AbstractApplicationContext(ApplicationContext parent) {this.parent = parent;this.resourcePatternResolver = getResourcePatternResolver(); } protected ResourcePatternResolver getResourcePatternResolver() {return new PathMatchingResourcePatternResolver(this); } public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {Assert.notNull(resourceLoader, "ResourceLoader must not be null");this.resourceLoader = resourceLoader; }setConfigLocations(configLocations)
這行代碼,主要就是初始化configLocations這個數組字段,源碼如下:
private String[] configLocations; public void setConfigLocations(String[] locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");this.configLocations = new String[locations.length];for (int i = 0; i < locations.length; i++) {this.configLocations[i] = resolvePath(locations[i]).trim();}}else {this.configLocations = null;} }其中resolvePath主要解析并填充資源路徑中的一些系統占位符,
如開始符:${,
結束符: }
分割符: :
refresh()
前面兩步基本上都是容器本身的設置初始化,這個步驟才是spring 容器解析,創建初始化bean的關鍵步驟,點時去,我們發現這個方法長,只分析xml解析的過程,其它的在這里不一 一細說,在refresh方法中,第二代碼是這樣的:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();直接進入org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()這個方法,看一下這個方法體:
@Override protected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}} catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);} }這個方法的大意是這樣的:
設置beanFactory的id,其中id是根據類名生成的,具體代碼是:
obj.getClass().getName() + "@" + getIdentityHexString(obj)5.解析xml文檔,并生成BeanDefinition對象實例集合;
重點看第5個步驟,即loadBeanDefinitions(beanFactory)這行代碼,根據繼承關系,直接進入org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory)這個方法
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.initBeanDefinitionReader(beanDefinitionReader);loadBeanDefinitions(beanDefinitionReader); }這個方法的邏輯也很清晰
針對每個資源文件,重點看下面這兩行代碼:
Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource);第一行代碼主是獲取路徑中以classpath:開頭的xml文件,當然也有很多其它前綴開頭的資源,org.springframework.util.ResourceUtils 這個類中有詳細的說明
第二主要是生成加載BeanDefinitions
前面都是一些準備過程,接下來,到了真正執行loadBeanDefinitions
DTD模式示例:
XSD模式示例:
重點看一下步驟3:
相關的方法源碼如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// 使用 DefaultBeanDefinitionDocumentReader實例化BeanDefinitionDocumentReader對象BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();// 記錄統計前已經加載的BeanDefinition數量int countBefore = getRegistry().getBeanDefinitionCount();// 加載并注冊beandocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 記錄本次加載的 BeanDefinition數量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();BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);preProcessXml(root);parseBeanDefinitions(root, delegate);postProcessXml(root); }registerBeanDefinitions這個方法的邏輯也比較清晰
重點看一下parseBeanDefinitions這個方法,方法實現源碼如下:
這個方法主要是針對bean的xml配置文件中默認標簽和自定義標簽分別進行解析
默認標簽的解析方法parseDefaultElement的方法體如下:
到這個方法,我們終于看到與我們平時寫spring bean配置文件相關的代碼,下面結bean標簽的解析處理代碼進行分析
bean標簽的解析最終會到parseBeanDefinitionElement方法,這個方法的部分代碼如下:
這個方法的主要工作內容包括:
4.將獲取到的信息封裝到BeanDefinitionHolder的實例中。
步驟2中對其它標簽的解析過程部分源碼如下:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,BeanDefinition containingBean, AbstractBeanDefinition bd) {if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // scope屬性// Spring 2.x "scope" attributebd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // singleton 屬性error("Specify either 'scope' or 'singleton', not both", ele);}}else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {// Spring 1.x "singleton" attributebd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);}else if (containingBean != null) {// Take default from containing bean in case of an inner bean definition.bd.setScope(containingBean.getScope());}if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { // abstract屬性bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));}String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); // lazy-init屬性if (DEFAULT_VALUE.equals(lazyInit)) {lazyInit = this.defaults.getLazyInit();}bd.setLazyInit(TRUE_VALUE.equals(lazyInit));String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); //autowire屬性bd.setAutowireMode(getAutowireMode(autowire));String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);bd.setDependencyCheck(getDependencyCheck(dependencyCheck));if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { // depends-on屬性String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));}// autowire-candidate 屬性String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {String candidatePattern = this.defaults.getAutowireCandidates();if (candidatePattern != null) {String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));}}else {bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));}// primary 屬性if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));}// init-method屬性if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);if (!"".equals(initMethodName)) {bd.setInitMethodName(initMethodName);}}else {if (this.defaults.getInitMethod() != null) {bd.setInitMethodName(this.defaults.getInitMethod());bd.setEnforceInitMethod(false);}}// destroy-method屬性if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);if (!"".equals(destroyMethodName)) {bd.setDestroyMethodName(destroyMethodName);}}else {if (this.defaults.getDestroyMethod() != null) {bd.setDestroyMethodName(this.defaults.getDestroyMethod());bd.setEnforceDestroyMethod(false);}}// factory-method 屬性if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));}// factory-bean 屬性if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));}return bd; }// 解析購造函數constructor-arg標簽元素 public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {parseConstructorArgElement((Element) node, bd);}} } //解析property屬性 public void parsePropertyElements(Element beanEle, BeanDefinition bd) {NodeList nl = beanEle.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {parsePropertyElement((Element) node, bd);}} }其它的標簽解析過程請參考下面這個類org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
上面解析分析了默認標簽bean的源碼解析過程,下面再來看一下自定標簽的解析過程,方法位置:org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(Element, BeanDefinition)源碼如下:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {//獲取對應的命名空間String namespaceUri = getNamespaceURI(ele);// 根據命名空間得到對相應的NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}// 調用自定義的NamespaceHandler handler進行解析return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }由于spring自定義標簽大家在平時用得比較少,由于時間關系和篇幅關系,本來就不對自定義標簽進行詳細分析,下一篇文章會結合spring源碼對自定義標簽的使用和解析原理進行詳細的介紹分析
由于時間關系,文中有些地方沒有寫細致,講得不夠清楚,可能不少地方還會出現低級的錯誤,請大家指正。
總結
以上是生活随笔為你收集整理的spring 源码分析(1)-xml文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用用户自定义类型作为map的key
- 下一篇: 【吾日三省吾身】2015.6.03-涅槃