参考资料:
《Spring IoC源码学习:obtainFreshBeanFactory 详解》
前文:
《Spring系列:从ContextLoaderListener到AbstractApplicationContext》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
目录
2、AbstractBeanDefinitionReade类
一、概述
obtainFreshBeanFactory 方法会解析所有 Spring 配置文件(即我们在前文中介绍的contextConfigLocation配置),将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,加载到 BeanFactory 中。
如果使用的是注解方式,在解析到<context:component-scan base-package="" /> 注解时,会扫描 base-package 指定的目录,将该目录下使用指定注解的 bean 定义也同样封装成 BeanDefinition,加载到 BeanFactory 中。
1、重要属性:
1、beanDefinitionNames
所有被加载到 BeanFactory 中的 bean 的 beanName 集合
2、beanDefinitionMap
所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射
2、源码
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新 BeanFactory,由AbstractRefreshableApplicationContext实现
refreshBeanFactory();
// 拿到刷新后的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
刷新 BeanFactory,由 AbstractRefreshableApplicationContext 实现
@Override
protected final void refreshBeanFactory() throws BeansException {
// 判断是否已经存在 BeanFactory,如果存在则先销毁、关闭该 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建一个新的BeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 定制beanFactory,设置相关属性,有2个重要属性:
// 1. allowBeanDefinitionOverriding:是否允许覆盖同名称的不同定义的对象
// 2. allowCircularReferences:是否允许bean之间的循环依赖
customizeBeanFactory(beanFactory);
// 加载 bean 定义。
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
二、loadBeanDefinitions
1、XmlWebApplicationContext类
1.1、loadBeanDefinitions
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为给定的beanFactory创建一个XmlBeanDefnitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 使用此上下文的资源加载环境配置 XmlBeanDefinitionReader
beanDefinitionReader.setEnvironment(getEnvironment());
// resourceLoader赋值为XmlWebApplicationContext
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化reader
initBeanDefinitionReader(beanDefinitionReader);
// 加载beanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
1.2、loadBeanDefinitions
这一步会根据我们在前文ApplicationContext刷新步骤中置入的configLocations判断从哪里加载spring的配置文件,如果web.xml中未配置contextConfigLocation参数,则会从默认路径/WEB-INF/applicationContext.xml下获取。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
// 获取配置文件路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
// 根据配置文件路径加载 bean 定义
reader.loadBeanDefinitions(configLocation);
}
}
}
// AbstractRefreshableWebApplicationContext.java
@Override
public String[] getConfigLocations() {
return super.getConfigLocations();
}
// AbstractRefreshableConfigApplicationContext.java
protected String[] getConfigLocations() {
// 判断前置步骤是否获取到了配置的configLocations参数,没有则从默认路径获取
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
// XmlWebApplicationContext.java
@Override
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[]{DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
} else {
return new String[]{DEFAULT_CONFIG_LOCATION};
}
}
2、AbstractBeanDefinitionReade类
2.1、loadBeanDefinitions
该步骤使用XmlWebApplicationContext从前一步骤获取到的configLocation中获取spring配置文件的路径,并封装成 Resource。
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 获取 resourceLoader,这边为 XmlWebApplicationContext
// 上文中beanDefinitionReader.setResourceLoader(this)时赋值
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
// 判断 resourceLoader 是否为 ResourcePatternResolver 的实例
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// 根据路径拿到该路径下所有符合的配置文件,并封装成Resource
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 根据Resource,加载Bean的定义
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
} else {
// Can only load single resources by absolute URL.
// 通过绝对路径加载单个资源
Resource resource = resourceLoader.getResource(location);
// 根据Resource,加载Bean的定义
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
2.2、loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
// 遍历所有的Resource
for (Resource resource : resources) {
// 根据Resource加载bean的定义,XmlBeanDefinitionReader实现
counter += loadBeanDefinitions(resource);
}
return counter;
}
3、 XmlBeanDefinitionReader类
3.1、loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 加载 bean 定义
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 当前正在加载的EncodedResource
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 将当前encodedResource添加到currentResources
if (!currentResources.add(encodedResource)) {
// 如果添加失败,代表当前的encodedResource已经存在,则表示出现了循环加载
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 拿到Resource的inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 将InputStream封装成一个InputSource,InputSource是xml SAX解析的数据源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 加载 bean 定义(方法以do开头,真正处理的方法)
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();
}
}
}
3.2、doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 根据inputSource和resource加载XML文件,并封装成Document
Document doc = doLoadDocument(inputSource, resource);
// 根据返回的Document注册Bean信息(对配置文件的解析,核心逻辑)
return registerBeanDefinitions(doc, resource);
} 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);
}
}
3.3、doLoadDocument
doLoadDocument方法将inputSource和resource封装成Document
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
// getValidationModeForResource(resource): 获取XML配置文件的验证模式
// documentLoader.loadDocument: 加载XML文件,并得到对应的 Document
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
getValidationModeForResource(resource)用于校验xml格式是否正确,这里略过,有兴趣的可以看这里《传送门》
// DefaultDocumentLoader.java
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
// 创建DocumentBuilderFactory
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 通过DocumentBuilderFactory创建DocumentBuilder
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 使用DocumentBuilder解析inputSource返回Document对象
return builder.parse(inputSource);
}
三、registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 记录统计前BeanDefinition个数
int countBefore = getRegistry().getBeanDefinitionCount();
// createReaderContext:根据resource创建一个XmlReaderContext
// registerBeanDefinitions:加载及注册Bean定义
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 计算当前BeanDefinition个数-加载前BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
1、createReaderContext
public XmlReaderContext createReaderContext(Resource resource) {
// 根据 resource 构建一个 XmlReaderContext,用于存放解析时会用到的一些上下文信息
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
// 创建默认的 DefaultNamespaceHandlerResolver
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
// DefaultNamespaceHandlerResolver.java
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
// handlerMappingsLocation有个重要属性handlerMappings
// 用于存放命名空间和该命名空间handler类的映射
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
2、registerBeanDefinitions
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获取到XML的根元素:<beans/>
Element root = doc.getDocumentElement();
// 通过拿到的节点,注册 Bean 定义
doRegisterBeanDefinitions(root);
}
3、doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
// 构建BeanDefinitionParserDelegate
this.delegate = createDelegate(getReaderContext(), root, parent);
// 校验root节点的命名空间是否为默认的命名空间(默认命名空间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);
// 校验当前节点的 profile 是否符合当前环境定义的, 如果不是则直接跳过, 不解析该节点下的内容
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// preProcessXml和postProcessXml都是模板方法,默认实现是空的
// 留给子类进行扩展,子类中可以处理一些自定义的bean
// 并把这些bean转换为标准的Spring BeanDefinition
preProcessXml(root);
// 解析<beans/>下的BeanDefinition
parseBeanDefinitions(root, this.delegate);
// 解析后处理, 留给子类实现
postProcessXml(root);
this.delegate = parent;
}
关于profile属性,可以看这段描述
4、parseBeanDefinitions
如果节点的命名空间是 Spring 默认的命名空间,则走 parseDefaultElement(ele, delegate) 方法进行解析,例如最常见的:<bean>。
如果节点的命名空间不是 Spring 默认的命名空间,也就是自定义命名空间,则走 delegate.parseCustomElement(ele) 方法进行解析,例如常见的: <context:component-scan/>、<aop:aspectj-autoproxy/>。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 默认命名空间的处理
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
// 遍历root的子节点列表
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)) {
// 默认命名空间节点的处理,例如: <bean id="test" class="" />
parseDefaultElement(ele, delegate);
}
else {
// 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
delegate.parseCustomElement(ele);
}
}
}
} else {
// 自定义命名空间的处理
delegate.parseCustomElement(root);
}
}
总结:
1、创建BeanFactory,类型为DefaultListableBeanFactory
2、从contextConfigLocation中读取Spring的xml配置文件,验证并封装成Resource
3、通过Resource加载 XML 配置文件,并解析成 Document 对象
4、拿到 Document 中的根节点,遍历根节点和所有子节点