BeanDefinitionReader BeanDefinitionReader 的作用是读取 Spring配置文件中的内容,将之解析为BeanDefinition并注册到BeanDefinitionRegistry工厂中。所以接口中定义的基本方法包括获取Bean定义注册工厂,加载Bean定义资源文件等方法。
源码 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public  interface  BeanDefinitionReader  {       BeanDefinitionRegistry getRegistry () ;        @Nullable     ResourceLoader getResourceLoader () ;        @Nullable     ClassLoader getBeanClassLoader () ;        BeanNameGenerator getBeanNameGenerator () ;        int  loadBeanDefinitions (Resource resource)  throws  BeanDefinitionStoreException;        int  loadBeanDefinitions (Resource... resources)  throws  BeanDefinitionStoreException;        int  loadBeanDefinitions (String location)  throws  BeanDefinitionStoreException;        int  loadBeanDefinitions (String... locations)  throws  BeanDefinitionStoreException; } 
 
BeanDefinitionReader实现类 类图如下图所示,可以看到 BeanDefinitionReader 下有一个抽象子类 AbstractBeanDefinitionReader,AbstractBeanDefinitionReader下有三个子类。
EnvironmentCapable:指示包含和公开Environment引用的组件的接口。 
AbstractBeanDefinitionReader:为 BeanDefinitionReader 接口的抽象实现类,实现了 EnvironmentCapable,提供了获取/设置环境的方法 
XmlBeanDefinitionReader:读取 XML 文件定义的 BeanDefinition 
PropertiesBeanDefinitionReader:可以从属性文件,Resource,Property 对象等读取 BeanDefinition 
GroovyBeanDefinitionReader:可以读取 Groovy 语言定义的 Bean 
 
AbstractBeanDefinitionReader 该类实现了 BeanDefinitionReader 和 EnvironmentCapable 接口的抽象类,提供属性:注册 bean 定义的bean 工厂、资源加载器、加载 bean 类的类加载器、环境、BeanName 生成器。
源码 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public  abstract  class  AbstractBeanDefinitionReader  implements  BeanDefinitionReader , EnvironmentCapable {       protected  final  Log  logger  =  LogFactory.getLog(getClass());        private  final  BeanDefinitionRegistry registry;        @Nullable     private  ResourceLoader resourceLoader;        @Nullable     private  ClassLoader beanClassLoader;        private  Environment environment;        private  BeanNameGenerator  beanNameGenerator  =  DefaultBeanNameGenerator.INSTANCE;          	 	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 (); 		} 	} } 
 
该类中最核心的方法是 loadBeanDefinitions()方法,首先查看覆盖父接口的三个方法:
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    @Override  public  int  loadBeanDefinitions (String... locations)  throws  BeanDefinitionStoreException {  		Assert.notNull(locations, "Location array must not be null" );   		int  count  =  0 ;   		for  (String location : locations) {       		      		count += loadBeanDefinitions(location);   		}   		return  count; } @Override public  int  loadBeanDefinitions (String location)  throws  BeanDefinitionStoreException {	return  loadBeanDefinitions(location, null ); } @Override public  int  loadBeanDefinitions (Resource... resources)  throws  BeanDefinitionStoreException {	Assert.notNull(resources, "Resource array must not be null" ); 	int  count  =  0 ; 	for  (Resource resource : resources) {             		count += loadBeanDefinitions(resource); 	} 	return  count; } 
 
本类中新增的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public  int  loadBeanDefinitions (String location, @Nullable  Set<Resource> actualResources)  throws  BeanDefinitionStoreException {       ResourceLoader  resourceLoader  =  getResourceLoader();        if  (resourceLoader == null ) {       throw  new  BeanDefinitionStoreException (             "Cannot load bean definitions from location ["  + location + "]: no ResourceLoader available" );    }    if  (resourceLoader instanceof  ResourcePatternResolver) {              try  {                    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);                    int  count  =  loadBeanDefinitions(resources);          if  (actualResources != null ) {             Collections.addAll(actualResources, resources);          }          if  (logger.isTraceEnabled()) {             logger.trace("Loaded "  + count + " bean definitions from location pattern ["  + location + "]" );          }          return  count;       }       catch  (IOException ex) {          throw  new  BeanDefinitionStoreException (                "Could not resolve bean definition resource pattern ["  + location + "]" , ex);       }    }    else  {              Resource  resource  =  resourceLoader.getResource(location);              int  count  =  loadBeanDefinitions(resource);       if  (actualResources != null ) {          actualResources.add(resource);       }       if  (logger.isTraceEnabled()) {          logger.trace("Loaded "  + count + " bean definitions from location ["  + location + "]" );       }       return  count;    } } 
 
根据资源加载器的不同,来处理资源路径,从而返回多个或一个资源,然后再将资源作为参数传递给 loadBeanDefinitions(Resource... resources)方法,该方法用于处理多个资源,归根结底,最后还是调用 BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理 。
XmlBeanDefinitionReader 该类作为 AbstractBeanDefinitionReader 的扩展类,继承了 AbstractBeanDefinitionReader 所有的方法,同时也扩展了很多新的方法,主要用于读取 XML 文件中定义的 bean。具体使用如下:
新建application-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0"  encoding="UTF-8" ?> <beans  xmlns ="http://www.springframework.org/schema/beans"  	   xmlns:context ="http://www.springframework.org/schema/context"  	   xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  	   xsi:schemaLocation ="http://www.springframework.org/schema/beans  	   https://www.springframework.org/schema/beans/spring-beans.xsd 	   http://www.springframework.org/schema/context 	   https://www.springframework.org/schema/context/spring-context.xsd 		" >	<bean  id ="user"  class ="com.wch.study.spring.entity.User"  name ="user4,user5;user6" >  	</bean >  	<alias  name ="user"  alias ="user2" />  	<alias  name ="user5"  alias ="user3" />  </beans > 
 
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public  void  xmlBeanDefinitionReaderTest () {   DefaultListableBeanFactory beanFactory=new  DefaultListableBeanFactory ();    AbstractBeanDefinitionReader beanDefinitionReader=new  XmlBeanDefinitionReader (beanFactory);    int  beanDefinitions  =  beanDefinitionReader.loadBeanDefinitions("application-context.xml" );    System.out.println("加载了:" +beanDefinitions+"个" );        User  user  =  (User) beanFactory.getBean("user" );    System.out.println("user:" +user);        System.out.println("user2:" +beanFactory.getBean("user2" ));    System.out.println("user3:" +beanFactory.getBean("user3" ));        String[] user2s = beanFactory.getAliases("user2" );    for  (String s : user2s) {       System.out.println(s);    } } 
 
输出:
加载了:1个 user:User{name=’null’, address=’null’} user2:User{name=’null’, address=’null’} user3:User{name=’null’, address=’null’} user user5 user3 user6 user4
 
解析 从我们写的这段代码进入  reader.loadBeanDefinitions("spring-config.xml"),上面已经提到最终会调用BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理,而我们指定的子类实现为XmlBeanDefinitionReader,调试进入子类实现,可以看到它加载Resource并封装为EncodedResource,并指定编码,当然它的 encoding、 charset 都是null。
1 2 3 4 5 6 7 public  int  loadBeanDefinitions (Resource resource)  throws  BeanDefinitionStoreException {        return  this .loadBeanDefinitions(new  EncodedResource (resource)); >1      }  public  EncodedResource (Resource resource)  {         this (resource, (String)null , (Charset)null );     } 
 
然后进入到loadBeanDefinitions(EncodedResource encodedResource) 方法,该方法主要根据encodedResource获得InputSource对象,并将InputSource对象参数和Resource对象参数传递给下层方法。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 	 private  final  ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =    new  NamedThreadLocal <Set<EncodedResource>>("XML bean definition resources currently being loaded" ){ 				@Override  				protected  Set<EncodedResource> initialValue ()  { 					return  new  HashSet <>(4 ); 				} 			}; public  int  loadBeanDefinitions (EncodedResource encodedResource)  throws  BeanDefinitionStoreException {		Assert.notNull(encodedResource, "EncodedResource must not be null" ); 		if  (logger.isTraceEnabled()) { 			logger.trace("Loading XML bean definitions from "  + encodedResource); 		} 		Set<EncodedResource> currentResources = this .resourcesCurrentlyBeingLoaded.get(); 		 		if  (!currentResources.add(encodedResource)) { 			throw  new  BeanDefinitionStoreException ( 					"Detected cyclic loading of "  + encodedResource + " - check your import definitions!" ); 		} 		 		try  (InputStream  inputStream  =  encodedResource.getResource().getInputStream()) { 			 			InputSource  inputSource  =  new  InputSource (inputStream); 			 			if  (encodedResource.getEncoding() != null ) { 				inputSource.setEncoding(encodedResource.getEncoding()); 			} 			 			return  doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 		} 		catch  (IOException ex) { 			throw  new  BeanDefinitionStoreException ( 					"IOException parsing XML document from "  + encodedResource.getResource(), ex); 		} 		finally  { 			 			currentResources.remove(encodedResource); 			if  (currentResources.isEmpty()) { 				this .resourcesCurrentlyBeingLoaded.remove(); 			} 		} 	} 
 
然后进入doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,该方法主要是将xml 资源文件转换为Document对象并根据Dcoument对象注册BeanDefinition
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 30 31 32 33 34 35 36 37 protected  int  doLoadBeanDefinitions (InputSource inputSource, Resource resource) 			throws  BeanDefinitionStoreException { 		try  { 			 			Document  doc  =  doLoadDocument(inputSource, resource); 			 			int  count  =  registerBeanDefinitions(doc, resource); 			if  (logger.isDebugEnabled()) { 				logger.debug("Loaded "  + count + " bean definitions from "  + resource); 			} 			return  count; 		} 		catch  (BeanDefinitionStoreException ex) { 			throw  ex; 		} 		catch  (SAXParseException ex) { 			throw  new  XmlBeanDefinitionStoreException (resource.getDescription(), 					"Line "  + ex.getLineNumber() + " in XML document from "  + resource + " is invalid" , ex); 		} 		catch  (SAXException ex) { 			throw  new  XmlBeanDefinitionStoreException (resource.getDescription(), 					"XML document from "  + resource + " is invalid" , ex); 		} 		catch  (ParserConfigurationException ex) { 			throw  new  BeanDefinitionStoreException (resource.getDescription(), 					"Parser configuration exception parsing XML from "  + resource, ex); 		} 		catch  (IOException ex) { 			throw  new  BeanDefinitionStoreException (resource.getDescription(), 					"IOException parsing XML document from "  + resource, ex); 		} 		catch  (Throwable ex) { 			throw  new  BeanDefinitionStoreException (resource.getDescription(), 					"Unexpected exception parsing XML document from "  + resource, ex); 		} 	}	 
 
来看一下  doLoadDocument(InputSource inputSource, Resource resource)方法:
1 2 3 4 5 6 7 private  DocumentLoader  documentLoader  =  new  DefaultDocumentLoader ();protected  Document doLoadDocument (InputSource inputSource, Resource resource)  throws  Exception {		return  this .documentLoader.loadDocument(inputSource, getEntityResolver(), this .errorHandler, 				getValidationModeForResource(resource), isNamespaceAware()); >1  	} 
 
doLoadDocument 方法里最终调用的是 documentLoader.loadDocument ()方法。 该方法的调用,一共需要五个参数:
InputSource:要调用的资源文件。 
EntityResolver: 处理文件的验证方式。 
ErrorHandler: 错误处理器。 
validationMode:  XML 文件的验证模式。 
namespaceAware: 是否开启自动感知名称空间。 
 
从 documentLoader.loadDocument ()点击会进入到 DocumentLoader接口,该接口下只有一个实现:DefaultDocumentLoader。 具体的调用如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public  Document loadDocument (InputSource inputSource, EntityResolver entityResolver,       ErrorHandler errorHandler, int  validationMode, boolean  namespaceAware)  throws  Exception {       DocumentBuilderFactory  factory  =  createDocumentBuilderFactory(validationMode, namespaceAware);    if  (logger.isTraceEnabled()) {       logger.trace("Using JAXP provider ["  + factory.getClass().getName() + "]" );    }        DocumentBuilder  builder  =  createDocumentBuilder(factory, entityResolver, errorHandler);        return  builder.parse(inputSource); } 
 
通过DocumentBuilder的parse方法就会将 XML 里的配置解析为 Document对象。使用这个Document对象可以获取 XML 文件中的节点并且创建节点。
createDocumentBuilderFactory(validationMode, namespaceAware):创建DocumentBuilderFactory对象
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 30 protected  DocumentBuilderFactory createDocumentBuilderFactory (int  validationMode, boolean  namespaceAware)       throws  ParserConfigurationException {        DocumentBuilderFactory  factory  =  DocumentBuilderFactory.newInstance();    factory.setNamespaceAware(namespaceAware);        if  (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {       factory.setValidating(true );              if  (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {                    factory.setNamespaceAware(true );          try  {             factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);          }          catch  (IllegalArgumentException ex) {             ParserConfigurationException  pcex  =  new  ParserConfigurationException (                   "Unable to validate using XSD: Your JAXP provider ["  + factory +                   "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? "  +                   "Upgrade to Apache Xerces (or Java 1.5) for full XSD support." );             pcex.initCause(ex);             throw  pcex;          }       }    }    return  factory; } 
 
createDocumentBuilder(factory, entityResolver, errorHandler):创建DocumentBuilder对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected  DocumentBuilder createDocumentBuilder (DocumentBuilderFactory factory,       @Nullable  EntityResolver entityResolver, @Nullable  ErrorHandler errorHandler)       throws  ParserConfigurationException {        DocumentBuilder  docBuilder  =  factory.newDocumentBuilder();        if  (entityResolver != null ) {       docBuilder.setEntityResolver(entityResolver);    }        if  (errorHandler != null ) {       docBuilder.setErrorHandler(errorHandler);    }    return  docBuilder; } 
 
 
接下来看doLoadBeanDefinitions方法中调用的registerBeanDefinitions(doc, resource)方法。
1 2 3 4 5 6 7 8 9 10 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; } 
 
这个方法中重点关注documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法。点击进入就会到 BeanDefinitionDocumentReader接口,该接口的作用就是定义读取 Docuemnt 并注册 BeanDefinition。
该接口就一个实现类 DefaultBeanDefinitionDocumentReader,接下来就看 DefaultBeanDefinitionDocumentReader 类中的 registerBeanDefinitions 方法。
1 2 3 4 5 @Override public  void  registerBeanDefinitions (Document doc, XmlReaderContext readerContext)  {   this .readerContext = readerContext;    doRegisterBeanDefinitions(doc.getDocumentElement()); } 
 
该方法有两个入参:
Document:代指 Spring 的配置文件信息,通过 BeanDefinitionReader 解析 Resrouce 实例得到。 
XmlReaderContext :主要包含了 BeanDefinitionReader 和 Resrouce 
 
然后进入doRegisterBeanDefinitions(doc.getDocumentElement())方法,进行解析
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 30 31 32 33 34 35 36 37 38 39 40 41 protected  void  doRegisterBeanDefinitions (Element root)  {                       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)) {             if  (logger.isDebugEnabled()) {                logger.debug("Skipped XML bean definition file due to specified profiles ["  + profileSpec +                      "] not matching: "  + getReaderContext().getResource());             }             return ;          }       }    }        preProcessXml(root);        parseBeanDefinitions(root, this .delegate);        postProcessXml(root);    this .delegate = parent; } 
 
来看其中最重要的方法 parseBeanDefinitions(root, this.delegate);
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 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);    } } 
 
而两种方式的读取及解析差别是非常大的,如果采用 Spring 默认的配置。Spring 当然知道该怎么做,但是如果是自定义的,那么就需要用户实现一些接口及配置,对于根节点或者子节点如果是默认命名空间的话,采用 parseDefaultElement 方法进行解析,否则使用delegate.parseCustomElement 方法对自定义命名空间进行解析。 而判断是否是默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI获取命名空间,并与 Spring 中固定的命名空间http://www.springframework.org/scherna/beans 进行比对,如果一致则认为是默认,否则就认为是自定义。
 
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 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);    } }