前言
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
看下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, SetactualResources) 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 MapbeanDefinitionMap = new ConcurrentHashMap (); 3 4 /** List of bean definition names, in registration order */ 5 private final List beanDefinitionNames = new ArrayList ();
最终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 SetcurrentResources = this.resourcesCurrentlyBeingLoaded.get(); 8 if (currentResources == null) { 9 currentResources = new HashSet (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行的方法得到根节点,也就是
剩下的就是第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 }
看到就是这个地方将
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 Listaliases = new ArrayList (); 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.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 41 aliases.add(beanClassName); 42 } 43 } 44 if (logger.isDebugEnabled()) { 45 logger.debug("Neither XML 'id' nor 'name' specified - " + 46 "using generated bean name [" + beanName + "]"); 47 } 48 } 49 catch (Exception ex) { 50 error(ex.getMessage(), ele); 51 return null; 52 } 53 } 54 String[] aliasesArray = StringUtils.toStringArray(aliases); 55 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 56 } 57 58 return null; 59 }
第2行~第18行的代码都是用于获取BeanName,从这段逻辑中我们可以总结出BeanName定义的规则:
- 默认的BeanName就是
标签重定义的id 标签中可以定义name属性,一个bean可以有多个别名(alias),都定义在name属性中,不同的别名以",;"分割,假如beanId未定义,那么就以name属性中的第一个别名作为beanName
第20行~第22行的代码主要用于确保BeanName的唯一性,跟一下第21行的方法就知道,BeanName与Bean别名都会放在Set
接着进入第24行的代码,开始解析Bean定义元素:
1 public AbstractBeanDefinition parseBeanDefinitionElement( 2 Element ele, String beanName, BeanDefinition containingBean) { 3 4 this.parseState.push(new BeanEntry(beanName)); 5 6 String className = null; 7 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { 8 className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); 9 } 10 11 try { 12 String parent = null; 13 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { 14 parent = ele.getAttribute(PARENT_ATTRIBUTE); 15 } 16 AbstractBeanDefinition bd = createBeanDefinition(className, parent); 17 18 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 19 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); 20 21 parseMetaElements(ele, bd); 22 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); 23 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); 24 25 parseConstructorArgElements(ele, bd); 26 parsePropertyElements(ele, bd); 27 parseQualifierElements(ele, bd); 28 29 bd.setResource(this.readerContext.getResource()); 30 bd.setSource(extractSource(ele)); 31 32 return bd; 33 } 34 catch (ClassNotFoundException ex) { 35 error("Bean class [" + className + "] not found", ele, ex); 36 } 37 catch (NoClassDefFoundError err) { 38 error("Class that bean class [" + className + "] depends on not found", ele, err); 39 } 40 catch (Throwable ex) { 41 error("Unexpected failure during bean definition parsing", ele, ex); 42 } 43 finally { 44 this.parseState.pop(); 45 } 46 47 return null; 48 }
对这个方法逐行总结一下:
- 第4行,ParseState是一个基于栈的简单结构,在解析的流程中用于追踪逻辑上的位置,里面存放的是Entry接口。Entry接口有各种实现类比如AdviceEntry、AdvisorEntry、BeanEntry、PropertyEntry等,每一步操作开始将一个Entry推送至栈顶,每一步操作结束将Entry从栈顶弹出,这里将一个BeanEntry推送至栈顶,标识解析Bean定义开始
- 第6行~第9行的代码,用于获取Bean对应的类路径
- 第12行~第15行的代码,用于解析
标签中的parent属性 - 第16行的代码,根据Bean对应的类路径以及parent,生成一个AbstractBeanDefinition,AbstractBeanDefinition是一个抽象类,生成出来的具体实例为GenericBeanDefinition
- 第18行的代码,就不跟了进去一看就很好理解,是用于解析Bean定义的属性的,包括scope、sington、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destory-method、factory-method、factory-bean,createHelper部分说了这个方法会创建一个名为defaults的DocumentDefaultsDefinition,像lazy-init、autowire-candidate、init-method、destory-method未定义时都会尝试从DocumentDefaultsDefinition中获取
- 第19行用于设置Bean描述
- 第21行的方法parseMetaElements用于解析META元素
- 第22行的方法parseLookupOverrideSubElements用于解析
标签下的 标签 - 第23行的方法parseReplacedMethodSubElements用于解析
标签下的 标签,不过这个和 标签好像不太常用 - 第25行的方法parseConstructorArgElements用于解析
标签下的 标签, 标签用于实现构造函数注入Bean属性 - 第26行的方法parsePropertyElements用于解析
标签下的 标签, 标签是最常见的Bean属性注入的方式 - 第27行的方法parseQualifierElements用于解析
标签下的 标签,使用 标签也是Spring属性注入的一种方式,不过不太常用
这样,就把整个Bean定义加载的流程跟完了,最后一步,就是将AbstractBeanDefinition写回到DefaultListableBeanFactory中了。
Bean定义加载流程----Bean定义写回DefaultListableBeanFactory
最后一步,将Bean定义写回DefaultListableBeanFactory中。代码要追溯回DefaultBeanDefinitionDocumentReader的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 }
Bean定义加载完毕后构造为一个BeanDefinitionHolder,第4行的代码之前说过的,用于在必要的情况下装饰Bean定义先不管。
第7行的代码用于注册Bean定义,跟一下代码:
1 public static void registerBeanDefinition( 2 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 3 throws BeanDefinitionStoreException { 4 5 // Register bean definition under primary name. 6 String beanName = definitionHolder.getBeanName(); 7 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 8 9 // Register aliases for bean name, if any. 10 String[] aliases = definitionHolder.getAliases(); 11 if (aliases != null) { 12 for (String aliase : aliases) { 13 registry.registerAlias(beanName, aliase); 14 } 15 } 16 }
跟一下第7行的方法,调用DefaultListableBeanFactory的registerBeanDefinition方法:
1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 2 throws BeanDefinitionStoreException { 3 4 Assert.hasText(beanName, "Bean name must not be empty"); 5 Assert.notNull(beanDefinition, "BeanDefinition must not be null"); 6 7 if (beanDefinition instanceof AbstractBeanDefinition) { 8 try { 9 ((AbstractBeanDefinition) beanDefinition).validate(); 10 } 11 catch (BeanDefinitionValidationException ex) { 12 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 13 "Validation of bean definition failed", ex); 14 } 15 } 16 17 synchronized (this.beanDefinitionMap) { 18 Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); 19 if (oldBeanDefinition != null) { 20 if (!this.allowBeanDefinitionOverriding) { 21 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 22 "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + 23 "': There is already [" + oldBeanDefinition + "] bound."); 24 } 25 else { 26 if (this.logger.isInfoEnabled()) { 27 this.logger.info("Overriding bean definition for bean '" + beanName + 28 "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); 29 } 30 } 31 } 32 else { 33 this.beanDefinitionNames.add(beanName); 34 this.frozenBeanDefinitionNames = null; 35 } 36 this.beanDefinitionMap.put(beanName, beanDefinition); 37 38 resetBeanDefinition(beanName); 39 } 40 }
简单说这个方法做了几件事情:
- beanDefinitionNames添加BeanName
- beanDefinitionMap添加一对映射,Key为BeanName,Value为Bean定义
- 第38行的方法用于重置一下所有本地缓存了的Bean定义
这两天又想到了两个细节问题,
1 if (beanDefinition != null) { 2 if (!StringUtils.hasText(beanName)) { 3 try { 4 if (containingBean != null) { 5 beanName = BeanDefinitionReaderUtils.generateBeanName( 6 beanDefinition, this.readerContext.getRegistry(), true); 7 } 8 else { 9 beanName = this.readerContext.generateBeanName(beanDefinition); 10 ... 11 }
当bean的id未定义时,即beanName为空,进入第2行的if判断。containingBean可以看一下,这里是由方法传入的,是一个null值,因此进入第9行的判断,即beanName由第9行的方法生成,看一下生成方式,代码最终要追踪到BeanDefinitionReaderUtils的generateBeanName方法:
1 public static String generateBeanName( 2 BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) 3 throws BeanDefinitionStoreException { 4 5 String generatedBeanName = definition.getBeanClassName(); 6 if (generatedBeanName == null) { 7 if (definition.getParentName() != null) { 8 generatedBeanName = definition.getParentName() + "$child"; 9 } 10 else if (definition.getFactoryBeanName() != null) { 11 generatedBeanName = definition.getFactoryBeanName() + "$created"; 12 } 13 } 14 if (!StringUtils.hasText(generatedBeanName)) { 15 throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " + 16 "'class' nor 'parent' nor 'factory-bean' - can't generate bean name"); 17 } 18 19 String id = generatedBeanName; 20 if (isInnerBean) { 21 // Inner bean: generate identity hashcode suffix. 22 id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); 23 } 24 else { 25 // Top-level bean: use plain class name. 26 // Increase counter until the id is unique. 27 int counter = -1; 28 while (counter == -1 || registry.containsBeanDefinition(id)) { 29 counter++; 30 id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter; 31 } 32 } 33 return id; 34 }
这段代码的逻辑很容易看懂,即:
- 假如是innerBean(比如Spring AOP产生的Bean),使用【类全路径+#+对象HashCode的16进制】的格式来命名Bean
- 假如不是innerBean,使用【类全路径+#+数字】的格式来命名Bean,其中数字指的是,同一个Bean出现1次,只要该Bean没有id,就从0开始依次向上累加,比如a.b.c#0、a.b.c#1、a.b.c#2
接着看一下id重复的场景Spring的处理方式,重复id是这样的,Spring使用XmlBeanDefinitionReader读取xml文件,在这个类的doLoadBeanDefinitions的方法中:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } ... }
第5行的代码将xml解析成Document,这里的解析使用的是JDK自带的DocumentBuilder,DocumentBuilder处理xml文件输入流,发现两个
因此,结论就是:Spring不允许两个