上一篇介绍了如何下载spring源码,编译,及修改源码+注解的使用
spring容器的基本使用及xml配置属性的说明;
这篇文章来介绍下spring容器时如何加载解析xml配置到spring容器中的
首先从测试代码中看到
package com.wsj.spring;
import com.wsj.spring.bean.LookUpSayHello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("test.xml");
LookUpSayHello bean = app.getBean(LookUpSayHello.class);
bean.sayHello();
}
}
使用ClassPathXmlApplicationContext加载xml文件,下面跟进去,最终调到这个构造方法
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//这里可以根据不同的子类实现
// 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
setConfigLocations(configLocations);
if (refresh) {
/**
* 这里简单说下为什么是 refresh(),而不是 init() 这种名字的方法。
* 因为 ApplicationContext 建立起来以后,其实我们是可以通过调用 refresh() 这个方法重建的,这样会将原来的 ApplicationContext 销毁,
* 然后再重新执行一次初始化操作。
*/
refresh();// 核心方法
}
}
点击进入refresh()方法,会看到跳转到父类(其实时爷爷的爷爷类)
AbstractApplicationContext中,
@Override
public void refresh() throws BeansException, IllegalStateException {
// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 这步比较关键,这步完成后,配置文件就会解析成一个个Bean定义(BeanDefinition),注册到 BeanFactory 中,
// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//完成BeanFactoryPostProcessors 和BeanDefinitionRegistryPostProcessor的实例化,执行相应的postProcessor方法
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
//实例化所有beanPostProcessors实例到容器中
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
//国际化代码
// Initialize message source for this context.
initMessageSource();
//spring事件代码
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
//可以自定义的onRefresh()
// Initialize other special beans in specific context subclasses.
onRefresh();
//事件的监听
// Check for listener beans and register them.
registerListeners();
//初始化单实例bean。spring ioc的核心代码入口
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
//完成容器的刷新
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
下面来详细介绍下obtainFreshBeanFactory()方法 :
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//主要看这里刷新beanFactory()
refreshBeanFactory();
return getBeanFactory();
}
这里调到了子类:AbstractRefreshableApplicationContext的refreshBeanFactory()
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//判断beanfactory是否存在,如果存在销毁容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建beanfactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加载beandefinitions 采用的模板方法模式 xml和注解不同的实现
loadBeanDefinitions(beanFactory); //这里是模板方法模式,让子类实现
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
这里首先判断beanFactory是否存在,如果不为空就清空容器 并销毁容器。 其实也就是非空判断(判断方法)
/**
* Determine whether this context currently holds a bean factory,
* i.e. has been refreshed at least once and not been closed yet.
*/
protected final boolean hasBeanFactory() {
synchronized (this.beanFactoryMonitor) {
return (this.beanFactory != null);
}
}
这里主要要看的是loadBeanDefinitions(beanFactory)这个方法,从名字就能看出是加载beandefitions的。这里调用的是子类:AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建xmlbeanDefinitionRead解析器 用于读取xml解析为BeanDefinition 加载的beanDefinitionMap中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
//把xmlApplicationContext设置到xmlBeanDefinitionRead中,让beanDefinitionRead 包含springContent
beanDefinitionReader.setResourceLoader(this);
//设置要用于解析xml的SAX实体解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类提供阅读器的自定义初始化,
//然后继续实际加载bean定义。
initBeanDefinitionReader(beanDefinitionReader);
//加载BeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
再看loadBeanDefinitions(beanDefinitionReader);这里是使用XmlBeanDefinitionReader解析器解析文件
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//获取所有要解析的xml文件列表
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//让BeanDefinitionRead去加载BeanDefinitions
reader.loadBeanDefinitions(configLocations);
}
}
这里要解析的xml文件列表是ClassPathXmlApplicationContext入口类的构造方法setConfigLocations(configLocations);中存进去的。
这里主要看的还是reader.loadBeanDefinitions(configLocations)这行代码。
这里调用了.XmlBeanDefinitionReader 的父类方法AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)
@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;
}
进入loadBeanDefinitions(location);中
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
进入loadBeanDefinitions(location, null);
/**
* Load bean definitions from the specified resource location.
* The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* @param location the resource location, to be loaded with the ResourceLoader
* (or ResourcePatternResolver) of this bean definition reader
* @param actualResources a Set to be filled with the actual Resource objects
* that have been resolved during the loading process. May be {@code null}
* to indicate that the caller is not interested in those Resource objects.
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #getResourceLoader()
* @see #loadBeanDefinitions(org.springframework.core.io.Resource)
* @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
*/
public int loadBeanDefinitions(String location, @Nullable Set 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) {
// Resource pattern matching available.
try {
//把xml解析成resource数组
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//BeanDefinitionRead加载BeanDefinitions
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.
//解析成resource的流文件
Resource resource = resourceLoader.getResource(location);
//BeanDefinitionRead加载BeanDefinitions 这里是模板方法,调用子类
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
这给方法可以看出是把xml文件解析成了Resource
接下来还是主要看:loadBeanDefinitions 这里又是调用子类方法,子类可以实现自己的解析加载规则
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//对资源xml文件进行encoded后交
return loadBeanDefinitions(new EncodedResource(resource));
}
这里把resource包装成EncodeResource。
进入loadBeanDefinitions方法中
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
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);
}
//从ThreadLocation中获取 EncodedResource列表
Set currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//把xml文件读取到流中
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//执行BeanDefinitions解析
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();
}
}
}
这里 主要是把resource读取到流中,然后调用doLoadBeanDefinitions(inputSource, encodedResource.getResource());把流里的内容解析成beanDefitions
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//解析xml流文件为Document 格式的
Document doc = doLoadDocument(inputSource, resource);
//注册BeanDefinitions
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);
}
}
这个方法是用于把流文件解析成Document。然后待用registerBeanDefinitions(doc, resource);方法,从dom文件中解析注册beanDefition。
下面看看registerBeanDefinitions(doc, resource);方法:
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建BeanDefinition的Document 解析器 委托模式,职责单一
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//createReaderContext 创建 XmlReaderContext 4*
//registerBeanDefinitions
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这里又创建了一个 BeanDefinitionDocumentReader 用于解析dom :
这里要看的是:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
首先看下createReaderContext(resource)方法
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
* 创建XML读取上线文
* 包含xml资源内容
* problemReporter失败记录容器
* eventListener事件监听
* sourceExtractor源提取器
* this
* getNamespaceHandlerResolver默认命名空间处理程序解析器
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
/**
* Lazily create a default NamespaceHandlerResolver, if not set before.
* @see #createDefaultNamespaceHandlerResolver()
*/
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
/**
* Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
* The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
* @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
* 这里初始化 默认命名空间处理程序解析器 加载 META-INF/spring.handlers
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
先写到这,后面的代码比较关键,抽空补齐