BeanDefinitionReader
- 读取BeanDefinition
- 借助BeanDefinitionRegistry将BeanDefinition注册到容器中
接口清单
public interface BeanDefinitionReader {
// 获取BeanDefinitionRegistry对象,将bd注册进容器中
BeanDefinitionRegistry getRegistry();
// 获取ResourceLoader
ResourceLoader getResourceLoader();
// 获取类加载器
ClassLoader getBeanClassLoader();
// Bean的名字生成器,为没有指定name的bean生成一个名字
BeanNameGenerator getBeanNameGenerator();
// 加载单个resource
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
// 加载多个resource
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
// 加载单个配置文件路径
// 该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
// 加载多个配置文件路径
int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}
UML

-
AbstractBeanDefinitionReader
:BeanDefinitionReader接口的抽象实现类,其中,加载多个资源的方法为org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
。同时,他还实现了EnvironmentCapable,提供了获取/设置环境的能力。 -
XmlBeanDefinitionReader
: 主要用于读取 XML 文件,将元素加载到 BeanDefinition. -
PropertiesBeanDefinitionReader
:提供Map / Properties和ResourceBundle的bean定义注册方法。通常应用于DefaultListableBeanFactory
导读
-
概览
概览
-
细览
细览
核心方法
loadBeanDefinitions
获取ResourceLoader的实例,然后判断该实例是属于ResourcePatternResolver还是默认的ResourceLoader实例。ResourcePatternResolver代表加载多个资源,否则加载单个资源。在确认加载多个还是单个后,都会调用loadBeanDefinitions(Resource resource)进行资源加载。
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 获取资源加载器,主要的功能是根据路径和类加载器获取Resource资源
ResourceLoader resourceLoader = getResourceLoader();
// 判断资源加载器是否为空
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
// ResourcePatternResolver用于加载多个文件或者能够加载 ant风格路径的文件资源
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
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 {
// Can only load single resources by absolute URL.
// 加载单个资源文件,通过绝对路径,直接使用ResourceLoader加载
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;
}
}
XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)
在AbstractBeanDefinitionReader并不会实现这个方法(抽象类 实现接口时,可以不实现部分接口方法),而是交由子类去做最终的实现。这里介绍的是经典的xml配置通过XmlBeanDefinitionReader加载到BeanDefinition的过程.
- 1.启动xml容器

在启动前,通过IDEA将断点打在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions上.
-
2. 查看堆栈信息
通过调用堆栈,我们可以看到,加载资源的步骤为(从下往上看):

- 容器的
refresh()
方法. -
AbstractXmlApplicationContext
的loadBeanDefinitions
.
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.setEnvironment(this.getEnvironment());
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);
}
这里说明一下,我们通常传入到容器的都是加载资源的位置,即configLocations
,Spring通过解析这个字符串得到ResourLoader,再通过ResourceLoader和BeanDefinitionReader得到BeanDefinition.

AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)
这里稍微简单,就是通过传入的多个location,循环加载BeanDefinition.
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;
}
AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
看到这里就是上面的抽象类通用方法了,由于当前容器是FileSystemXmlApplicationContext,它扩展的上层接口中有ApplicationContext
,通过我的前几篇文章你可以得知,ApplicationContext
接口是扩展了ResourcePatternResolver
接口的,由此在AbstractBeanDefinitionReader
中就会将当前的ResourceLoader
视作为ResourcePatternResolver
的实例。
AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)
上面说明了,如果是ResourcePatternResolver
,会进行多个资源加载。在这里就会进入到XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)
中.

XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
加载Resource,并指定编码
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
Spring使用了ThreadLocal来存储加载的resource,进而使用多线程加载资源,这使得多个线程之间加载的resource互不冲突.
// 用ThreadLocal来存储正在加载的资源
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
new NamedThreadLocal<>("XML bean definition resources currently being loaded");
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);
}
// 多线程加载Resource,每个本地线程存储自己正在加载的resource,其他线程无法访问和修改
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
// 如果本地线程变量中不存在正在加载的资源,那么将其添加进去
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 如果往currentResources添加encodedResource失败,表明已经存在资源,只不过未加载完成,防止同一个线程多次加载同一个文件
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 获取文件的输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 封装成InputSource,其中指定了输入流和编码格式
InputSource inputSource = new InputSource(inputStream);
// 如果存在编码,那么将其添加进InputSource中
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 继续加载 bd
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
-
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
-
Document
:通过debug,可以从Document的fNodeValue看到内容。
-

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 创建Document对象,XML的文档对象,就是DOM树
// 使用这个Document可以获取XML文件中的节点并且创建节点
// 使用 SAX的方式解析XML
Document doc = doLoadDocument(inputSource, resource);
// 解析dom树,即解析出一个个属性,然后将其保存到bd中
// 向容器注册bd
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);
}
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
获取BeanDefinitionDocumentReader,读取配置到BeanDefinition中,然后向容器注册BeanDefinition,可以看到有前后对比注册的BeanDefinition的逻辑.在这里,getRegistry()指向的是DefaultListableBeanFactory
,getBeanDefinitionCount()指向的是DefaultListableBeanFactory
容器的beanDefinitionMap.size()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader,这个实际从XML的DOM树中读取bd
// 实际进行工作的是 DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 未注册前的bd数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 注册bd
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 注册后的bd数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
DefaultBeanDefinitionDocumentReader开始解析xml文件->doRegisterBeanDefinitions
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 根据符合spring DTD 的 xml 解析 BeanDefinition
doRegisterBeanDefinitions(doc.getDocumentElement());
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
在这里,DefaultBeanDefinitionDocumentReader将解析xml的任务委托给了BeanDefinitionParserDelegate进行。这里关注一个真正解析xml的方法->
parseBeanDefinitions(root, this.delegate)
;
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
// bd解析委托类
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 判断root节点是否是默认的命名空间
// nameSpaceUrl == "http://www.springframework.org/schema/beans"
if (this.delegate.isDefaultNamespace(root)) {
// 获取profile属性的值,用于区别不同的环境
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
// 根据分隔符转换成数组
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
// 判断这个切面是否是active的环境,如果不是直接返回,表示这个配置文件不是当前环境的配置文件
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;
}
}
}
// 在解析xml之前的准备工作
preProcessXml(root);
// 解析xml
parseBeanDefinitions(root, this.delegate);
// 解析完xml后的钩子方法
postProcessXml(root);
this.delegate = parent;
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
从根结点开始遍历子结点,对子结点进行解析。
parseDefaultElement(ele,delegate)
则是Spring解析符合其命名规则的逻辑了.
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;
// 如果符合Spring的命名规则,对该标签进行解析
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
// 解析用户自定义的规则
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement
对于每个子结点,也有可能出现<beans/>标签,Spring会进行递归调用。这里关注bean的解析-> processBeanDefinition(ele, delegate);
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果该节点名称等于"import",即<import resource="classpath:xxx.xml"/>,对xml文件进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 如果该节点名称为"alias",即<Alias>,对bean别名进行解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 如果该节点名称为"bean",即最常见的<Bean>,进行Bean解析
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 递归调用doRegisterBeanDefinitions,再次重复解析xml的过程
doRegisterBeanDefinitions(ele);
}
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition
parseBeanDefinitionElement(ele)就是开始解析bean标签的内容了,拨开层层迷雾,终于看到了解析bean标签的地方。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder->对BeanDefinition的封装
// 对Document对象中<bean>元素的解析委托给BeanDefinitionParserDelegate实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向容器注册的入口
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 向容器发送注册事完成件通知
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
使用GenericBeanDefinition进行Bean属性的装载->AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取id
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
// 将bean元素的name放入别名数组中,spring支持两种方式定义别名,一种是通过<Alias>,另一种是<bean name=""/>
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 检测<bean>中的name和id是否唯一
checkNameUniqueness(beanName, aliases, ele);
}
// 对bean标签的其他属性进行解析,5.1X版本中,Spring使用了GenericBeanDefinition来解析BeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 赋予名称
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 将class与parentName作为参数创建BeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析bean中的属性值,通过set方法将属性进行注入
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
// look up method
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// replace method
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition
联系之前学习的GenericBeanDefinition,这里可以考证,Spring的确是用GenericBeanDefinition对Bean进行定义的。联系上面的代码块,可以看到Spring创建了GenericBeanDefinition并解析其中的标签内容,将这些属性注入到GenericBeanDefinition中.
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
-
至此,我们可以看到Spring加载xml并装配到BeanDefinition的详细过程了,完成这一部之后,Spring就会通过
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
将每个BeanDefinition注册到容器中 -
发送事件通知容器,完成了一个bean的注册事件
-
refresh中的obtainFreshBeanFactory()方法执行完毕,容器继续其他的行为
总结
- BeanDefinitionReader是解析BeanDefinition的执行者
- BeadDefinitionReader使用ResourceLoader进行资源定位
- GenericBeanDefinition为真正装载BeanDefinition属性的类
- 完成BeanDefinition的装载后,BeanDefinitionReader会对该BeanDefinition进行注册
- 完成注册后,Spring会发送注册完成通知事件
网友评论