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); } }