Spring源码分析配置文件读取流程
Posted 五月的仓颉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码分析配置文件读取流程相关的知识,希望对你有一定的参考价值。
前言
Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spring配置文件读取流程还是研究得不够,因此将Spring配置文件读取流程部分从之前的文章拆出来单独成为一文。
为了看一下Spring配置文件加载流程,先定义一个bean.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 6 7 <bean id="beanPostProcessorBean" class="org.xrq.action.BeanPostProcessorBean" /> 8 9 <bean id="beanFactoryPostProcessorBean" class="org.xrq.action.BeanFactoryPostProcessorBean" /> 10 11 <bean id="multiFunctionBean" class="org.xrq.action.MultiFunctionBean" init-method="initMethod"> 12 <property name="propertyA" value="abc" /> 13 </bean> 14 15 </beans>
至于Bean是什么并不重要,有配置文件就够了。
Bean定义加载流程----从Refresh到Bean定义加载前
首先看一下Bean加载前整个代码流程走向。Spring上下文刷新始于AbstractApplicationContext的refresh()方法:
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 // Prepare this context for refreshing. 4 prepareRefresh(); 5 6 // Tell the subclass to refresh the internal bean factory. 7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 8 9 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 12 ... 13 }
代码不全帖了,第7行的obtainFreshBeanFactory()方法进去:
1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 2 refreshBeanFactory(); 3 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 4 if (logger.isDebugEnabled()) { 5 logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); 6 } 7 return beanFactory; 8 }
第2行的refreshBeanFactory()方法进去,它是AbstractApplicationContext的子类AbstractRefreshableApplicationContext中的方法:
1 @Override 2 protected final void refreshBeanFactory() throws BeansException { 3 if (hasBeanFactory()) { 4 destroyBeans(); 5 closeBeanFactory(); 6 } 7 try { 8 DefaultListableBeanFactory beanFactory = createBeanFactory(); 9 beanFactory.setSerializationId(getId()); 10 customizeBeanFactory(beanFactory); 11 loadBeanDefinitions(beanFactory); 12 synchronized (this.beanFactoryMonitor) { 13 this.beanFactory = beanFactory; 14 } 15 } 16 catch (IOException ex) { 17 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 18 } 19 }
首先第8行获取DefaultListableBeanFactory,然后执行第11行的方法,传入当前获取的BeanFactory,准备加载Bean定义。BeanFactory中有存储了些什么数据在【Spring源码分析】Bean加载流程概览一文中有画表格详细说明,看过表格的朋友应该知道为什么第8行要获取的是DefaultListableBeanFactory而不是它的接口BeanFactory,因为Bean定义存储在Map<String, BeanDefinition>中,这个Map的位置就是在DefaultListableBeanFactory里,因此这里直接获取DefaultListableBeanFactory并作为参数层层向后传,加载完Bean定义后直接向Map<String, BeanDefinition>里put键值对。
看下loadBeanDefinitions方法,它是AbstractRefreshableApplicationContext子类AbstractXmlApplicationContext中的一个方法:
1 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 2 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 3 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 4 5 // Configure the bean definition reader with this context\'s 6 // resource loading environment. 7 beanDefinitionReader.setResourceLoader(this); 8 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 9 10 // Allow a subclass to provide custom initialization of the reader, 11 // then proceed with actually loading the bean definitions. 12 initBeanDefinitionReader(beanDefinitionReader); 13 loadBeanDefinitions(beanDefinitionReader); 14 }
第3行的XmlBeanDefinitionReader是Bean加载的核心类,先构建出来,后面代码没什么值得看的,直接看第13行代码,传入XmlBeanDefinitionReader:
1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { 2 Resource[] configResources = getConfigResources(); 3 if (configResources != null) { 4 reader.loadBeanDefinitions(configResources); 5 } 6 String[] configLocations = getConfigLocations(); 7 if (configLocations != null) { 8 reader.loadBeanDefinitions(configLocations); 9 } 10 }
由第8行的代码进去,这个就不跟了,直接走到XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的loadBeanDefinitions方法中:
1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { 2 ResourceLoader resourceLoader = getResourceLoader(); 3 if (resourceLoader == null) { 4 throw new BeanDefinitionStoreException( 5 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); 6 } 7 8 if (resourceLoader instanceof ResourcePatternResolver) { 9 // Resource pattern matching available. 10 try { 11 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); 12 int loadCount = loadBeanDefinitions(resources); 13 if (actualResources != null) { 14 for (Resource resource : resources) { 15 actualResources.add(resource); 16 } 17 } 18 if (logger.isDebugEnabled()) { 19 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); 20 } 21 return loadCount; 22 } 23 catch (IOException ex) { 24 throw new BeanDefinitionStoreException( 25 "Could not resolve bean definition resource pattern [" + location + "]", ex); 26 } 27 } 28 else { 29 // Can only load single resources by absolute URL. 30 Resource resource = resourceLoader.getResource(location); 31 int loadCount = loadBeanDefinitions(resource); 32 if (actualResources != null) { 33 actualResources.add(resource); 34 } 35 if (logger.isDebugEnabled()) { 36 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); 37 } 38 return loadCount; 39 } 40 }
我们研究Spring加载流程使用的ClassPathXmlApplicationContext是ResourcePatternResolver的实现类,进入第8行的判断,走第12行的方法,这里也不跟了,很简单,最终代码走到了XmlBeanDefinitionReader的loadBeanDefinitions方法中,也就是Bean定义加载的开始。
Bean定义加载流程----Bena定义的存储
上面说到了Bean定义是存储在DefaultListableBeanFactory中的,我们来看一下具体代码:
1 /** Map of bean definition objects, keyed by bean name */ 2 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); 3 4 /** List of bean definition names, in registration order */ 5 private final List<String> beanDefinitionNames = new ArrayList<String>();
最终DefaultListableBeanFactory会先遍历beanDefinitionNames,从beanDefinitionMap中拿到对应的BeanDefinition,最终转为具体的Bean对象。BeanDefinition本身是一个接口,AbstractBeanDefinition这个抽象类存储了Bean的属性,看一下AbstractBeanDefinition这个抽象类的定义:
这个类的属性与方法很多,这里就列举了一些最主要的方法和属性,可以看到包含了bean标签中的所有属性,之后就是根据AbstractBeanDefinition中的属性值构造出对应的Bean对象。
Bean定义加载流程----开始加载Bean定义
上面一部分的结尾说道,Bean定义加载的开始始于XmlBeanDefinitionReader的loadBeanDefinitions方法,看下loadBeanDefinitions方法定义:
1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 2 Assert.notNull(encodedResource, "EncodedResource must not be null"); 3 if (logger.isInfoEnabled()) { 4 logger.info("Loading XML bean definitions from " + encodedResource.getResource()); 5 } 6 7 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); 8 if (currentResources == null) { 9 currentResources = new HashSet<EncodedResource>(4); 10 this.resourcesCurrentlyBeingLoaded.set(currentResources); 11 } 12 if (!currentResources.add(encodedResource)) { 13 throw new BeanDefinitionStoreException( 14 "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); 15 } 16 try { 17 InputStream inputStream = encodedResource.getResource().getInputStream(); 18 try { 19 InputSource inputSource = new InputSource(inputStream); 20 if (encodedResource.getEncoding() != null) { 21 inputSource.setEncoding(encodedResource.getEncoding()); 22 } 23 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 24 } 25 finally { 26 inputStream.close(); 27 } 28 } 29 catch (IOException ex) { 30 throw new BeanDefinitionStoreException( 31 "IOException parsing XML document from " + encodedResource.getResource(), ex); 32 } 33 finally { 34 currentResources.remove(encodedResource); 35 if (currentResources.isEmpty()) { 36 this.resourcesCurrentlyBeingLoaded.remove(); 37 } 38 } 39 }
第17行根据XML文件获取输入字节流,接着流程走到23行doLoadBeanDefinitions方法:
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException { 3 try { 4 int validationMode = getValidationModeForResource(resource); 5 Document doc = this.documentLoader.loadDocument( 6 inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 7 return registerBeanDefinitions(doc, resource); 8 } 9 catch (BeanDefinitionStoreException ex) { 10 throw ex; 11 } 12 catch (SAXParseException ex) { 13 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 14 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); 15 } 16 catch (SAXException ex) { 17 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 18 "XML document from " + resource + " is invalid", ex); 19 } 20 catch (ParserConfigurationException ex) { 21 throw new BeanDefinitionStoreException(resource.getDescription(), 22 "Parser configuration exception parsing XML from " + resource, ex); 23 } 24 catch (IOException ex) { 25 throw new BeanDefinitionStoreException(resource.getDescription(), 26 "IOException parsing XML document from " + resource, ex); 27 } 28 catch (Throwable ex) { 29 throw new BeanDefinitionStoreException(resource.getDescription(), 30 "Unexpected exception parsing XML document from " + resource, ex); 31 } 32 }
首先是第4行,获取验证模式,代码不跟了,最终出来的是DetectMode,DetectMode的意思是XML文件的验证模式由XML文件本身决定,如果是DTD那就使用DTD验证,如果是XSD就使用XSD验证。
接着是第5行~第6行,这两行的作用是通过DOM得到org.w3c.dom.Document对象,Document将XML文件看成一棵树,Dociument即对这颗树数据结构的一个描述。
最近进入第7行,继续加载Bean定义的流程,跟一下registerBeanDefinitions方法:
1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 2 // Read document based on new BeanDefinitionDocumentReader SPI. 3 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 4 int countBefore = getRegistry().getBeanDefinitionCount(); 5 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 6 return getRegistry().getBeanDefinitionCount() - countBefore; 7 }
因为是每个XML文件执行一次registerBeanDefinitions方法注册Bean定义,因此这整个方法的返回值表示的是当前XML里面一共注册了多少个Bean。直接进入第5行的代码,使用BeanDefintionDocumentReader的registerBeanDefinitions方法来注册Bean定义:
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 2 this.readerContext = readerContext; 3 4 logger.debug("Loading bean definitions"); 5 Element root = doc.getDocumentElement(); 6 7 BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); 8 9 preProcessXml(root); 10 parseBeanDefinitions(root, delegate); 11 postProcessXml(root); 12 }
这里面的方法,第9行的方法preProcessXml是个空方法,留给子类扩展用;第11行的方法postProcessXml是个空方法,留给子类扩展用。
第5行的方法得到根节点,也就是<beans ...></beans>。
剩下的就是第7行的createHelper方法与第10行的parseBeanDefintions方法了,前者构造出一个Bean定义解析器的委托类,后者使用委托类解析Bean定义,下面分两部分分别来看。
Bean定义加载流程----createHelper
先看createHelper,即根据根节点创建一个Bean定义解析器的委托类,看一下代码实现:
1 protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) { 2 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); 3 delegate.initDefaults(root); 4 return delegate; 5 }
第2行没有什么特别的,new一个BeanDefinitionParserDelegate出来,第3行的代码跟一下,用于设置默认属性:
1 public void initDefaults(Element root) { 2 populateDefaults(this.defaults, root); 3 this.readerContext.fireDefaultsRegistered(this.defaults); 4 }
跟一下第2行的代码:
1 protected void populateDefaults(DocumentDefaultsDefinition defaults, Element root) { 2 defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE)); 3 defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE)); 4 defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE)); 5 defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); 6 if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { 7 defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); 8 } 9 if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { 10 defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); 11 } 12 if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { 13 defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); 14 } 15 defaults.setSource(this.readerContext.extractSource(root)); 16 }
看到就是这个地方将<beans>标签下的default-lazy-init、default_merge、default_autowire、default-dependency-check、default-autowire-candidates、default-init-method、default-destroy-method这几个属性取出来,设置到DocumentDefaultsDefinition即defaults中。
Bean定义加载流程----parseBeanDefintions
到了parseBeanDefintions方法了,这个方法开始真正遍历XML文件中的各个标签并转换为对应的Bean定义,看一下方法定义:
1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 2 if (delegate.isDefaultNamespace(root)) { 3 NodeList nl = root.getChildNodes(); 4 for (int i = 0; i < nl.getLength(); i++) { 5 Node node = nl.item(i); 6 if (node instanceof Element) { 7 Element ele = (Element) node; 8 if (delegate.isDefaultNamespace(ele)) { 9 parseDefaultElement(ele, delegate); 10 } 11 else { 12 delegate.parseCustomElement(ele); 13 } 14 } 15 } 16 } 17 else { 18 delegate.parseCustomElement(root); 19 } 20 }
首先说一下,这里的Namespace都是默认的Namespace,至于Namespace的问题,和自定义Spring标签相关,我想放到自定义Spring标签部分说,这里只要知道代码会进入第2行与第9行的判断即可。
第2行的判断进去,都在遍历Element下的节点不看了,直接跟第9行的代码parseDefaultElement:
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 3 importBeanDefinitionResource(ele); 4 } 5 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 6 processAliasRegistration(ele); 7 } 8 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 9 processBeanDefinition(ele, delegate); 10 } 11 }
这边就是判断节点名称是import还是alias还是bean,是其中任意一个就进入相应的执行逻辑,import和alias不看了,这里就看Bean加载流程部分,也就是第9行的processBeanDefinition方法:
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 2 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 3 if (bdHolder != null) { 4 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 5 try { 6 // Register the final decorated instance. 7 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 8 } 9 catch (BeanDefinitionStoreException ex) { 10 getReaderContext().error("Failed to register bean definition with name \'" + 11 bdHolder.getBeanName() + "\'", ele, ex); 12 } 13 // Send registration event. 14 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 15 } 16 }
先看第4行,第4行的意思是在需要的时候装饰Bean定义,比如AOP的场景会使用到,这个留在AOP的时候看这段代码。
再看第7行,第7行的意思是注册Bean定义,这在下一部分说,属于Bean定义加载流程的最后一步。
现在看来第2行的代码,顾名思义即解析Bean定义元素,跟一下代码:
1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { 2 String id = ele.getAttribute(ID_ATTRIBUTE); 3 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 4 5 List<String> aliases = new ArrayList<String>(); 6 if (StringUtils.hasLength(nameAttr)) { 7 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); 8 aliases.addAll(Arrays.asList(nameArr)); 9 } 10 11 String beanName = id; 12 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 13 beanName = aliases.remove(0); 14 if (logger.isDebugEnabled()) { 15 logger.debug("No XML \'id\' specified - using \'" + beanName + 16 "\' as bean name and " + aliases + " as aliases"); 17 } 18 } 19 20 if (containingBean == null) { 21 checkNameUniqueness(beanName, aliases, ele); 22 } 23 24 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 25 if (beanDefinition != null) { 26 if (!StringUtils.hasText(beanName)) { 27 try { 28 if (containingBean != null) { 29 beanName = BeanDefinitionReaderUtils.generateBeanName( 30 beanDefinition, this.readerContext.getRegistry(), true); 31 } 32 else { 33 beanName = this.readerContext.generateBeanName(beanDefinition); 34 // Register an alias for the plain bean class name, if still possible, 35 // if the generator returned the class name plus a suffix. 36 // This is expected for Spring 1.2/2.0 backwards compatibility. 37 String beanClassName = beanDefinition.getBeanClassName(); 38 if (beanClassName != null && 39 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 40 !this以上是关于Spring源码分析配置文件读取流程的主要内容,如果未能解决你的问题,请参考以下文章
Spring框架进阶Spring V3.0 IOC源码分析流程
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段