本节来学习IOC容器的初始化过程
IOC容器的初始化由refresh()方法启动的,具体包含BeanDefinition的Resource定位、载入和注册三个过程。
第一个过程是Resource定位过程。Resource定位指的是BeanDefinition的资源定位,由ResourceLoader通过统一的Resource接口来完成。
第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IOC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。
第三个过程是向IOC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现完成的。在IOC容器内部将BeanDefinition注入到一个HashMap中去。
1.BeanDefinition的Resource定位
Resource的结构图:
下面以FileSystemXmlApplicationContext为例子,学习资源定位过程。
从上图可以看出,FileSystemXmlApplicationContext已经通过继承AbstractApplicationContext具备了ResourceLoader读入以及Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是
DefaultResourceLoader。
下面看一下FileSystemXmlApplicationContext的具体实现:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
/**
* 这个构造函数的configLocation包含的是BeanDefinition所在的文件路径
*/
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
/**
* 这个构造函数允许configLocations包含多个BeanDefinition的文件路径
*/
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
/**
* 这个构造函数允许configLocations包含多个BeanDefinition的文件路径,且允许指定双亲IOC容器
*/
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
/**
* 在对象的初始化过程中,调用refresh函数载入BeanDefinition,这个refresh启动了BeanDefinition的载入过程
*/
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
/**
* 通过构造一个FileSystemResource得到一个在文件系统中定位的BeanDefinition,这个getResourceByPath是BeanDefinitionReader的loadBeanDefinition中被调用的。
*/
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
getResourceByPath方法,是一个模板方法,是为了读取Resource服务的。
下面重点看一下AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用。在这个方法中,通过createBeanFactory构建了一个IOC容器供ApplicationContext使用。这个IOC容器就是DefaultListableBeanFactory,同时,还启动了loadBeanDefinitions和载入BeanDefinition。
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
destroyBeans();
closeBeanFactory();
}
try {//创建并设置持有的DefaultListableBeanFactory的地方同时调用loadBeanDefinitions载入BeanDefinition的信息
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
/**
* 在上下文中创建DefaultListableBeanFactory,会根据已经有的双亲IOC容器生成DefaultListableBeanFactory的双亲IOC容器
*/
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
/**
* 使用BeanDefinitionReader载入Bean定义,具体的实现委托为子类
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
下面看一下AbstractBeanDefinitionReader的loadBeanDefinitions方法
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();//获取ResourceLoader,使用的是DefaultResourceLoader
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {//对Resource的路径模式进行解析
// Resource pattern matching available.
try {//调用DefaultResourceLoader的getResource完成具体的Resource定位
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
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 {
//调用DefaultResourceLoader的getResource完成具体的Resource定位
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);//BeanDefinition的载入
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
下面看一下DefaultResourceLoader的getResource过程
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//处理带有classpath标识的Resource
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 处理URL标识的Resource定位
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);//具体的子类实现,默认是ClassPathContextResource
}
}
}
2.BeanDefinition的载入和解析
从DefaultListableBeanFactory的设计入口,看一下IOC容器怎么完成BeanDefinition的载入的。
先来看一下容器初始化的入口refresh的实现:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
//子类中启动refreshBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 设置BeanFactory的后置处理
postProcessBeanFactory(beanFactory);
// 调用BeanFactory的后处理器,在Bean定义中向容器注册
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后处理器,在Bean创建过程中调用
registerBeanPostProcessors(beanFactory);
// 对上下文中的消息源进行初始化
initMessageSource();
// 初始化上下文中的事件机制
initApplicationEventMulticaster();
// 初始化其他的特殊Bean
onRefresh();
// 检查监听Bean并将这些Bean向容器注册
registerListeners();
// 实例化所有的非延时初始的单件
finishBeanFactoryInitialization(beanFactory);
// 发布容器事件,结束refresh过程
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 为了防止Bean资源占用,在异常中,销毁已经生成的单例Bean
destroyBeans();
// 重置 'active' 标识.
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();
}
}
}
前面有介绍了refreshBeanFactory方法,这里会启动对BeanDefinition的载入loadBeanDefinition,具体的实现是在AbstractXmlApplicationContext类,在这里初始化了读取器XmlBeanDefinitionReader,然后把读取器在IOC容器中设置好,最后是启动读取器完成BeanDefinition在IOC容器中的载入。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建XmlBeanDefinitionReader,通过回调设置到beanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置XmlBeanDefinitionReader配置ResourceLoader等信息
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 启动Bean定义信息载入的过程
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
具体的载入过程委托给BeanDefinitionReader完成的
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();//以Resource形式获取配置文件的资源位置
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();//以String形式获取配置文件的位置
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader载入BeanDefinition
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;//遍历集合所包含的BeanDefinition信息
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}
XmlBeanDefinitionReader的loadBeanDefinitions实现:
/**
* BeanDefinition载入的入口
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
* 载入XML形式的BeanDefinition
*/
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());
}
Set<EncodedResource> 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文件,并得到IO的InputStream准备进行读取
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
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();
}
}
}
/**
* 具体的读取过程在doLoadBeanDefinitions,从特定的XML文件中实际载入BeanDefinition
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {//获取XMl文件的Doucument对象
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);//这里启动对BeanDefinition解析的详细过程,这个解析会用到Spring的Bean配置规则。
}
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);
}
}
按照Spring的Bean语义要求进行解析并转化为容器内部数据结构:
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;
}
BeanDefinition的载入分为两部分,首先通过调用XML的解析器得到document对象,但这些document对象并没有按照Spring的Bean规则进行解析,按照Spring的Bean规则进行解析的过程是在documentReader中实现的。这里使用的documentReader是DefaultBeanDefinitionDocumentReader。完成对BeanDefinition的处理后,处理的结果由BeanDefinitionHolder对象持有。
/**
* 处理BeanDefinition,具体的处理委托给BeanDefinitionParserDelegate
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {//BeanDefinitionHolder是BeanDefinition对象的封装类,封装了BeanDefinition、Bean名字和别名
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 向IOC容器注册解析得到的BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 注册完成后,发送消息
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);//获取id
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//获取name
List<String> aliases = new ArrayList<>();//获取别名
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.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//对Bean元素的详细解析
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.isDebugEnabled()) {
logger.debug("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;
}
具体的过程不再继续研究,有兴趣的可以看一下parseBeanDefinitionElement的源码
3.BeanDefinition在IOC容器中的注册
前面分析了载入和解析过程,注册在DefaultListableBeanFactory中,通过一个HashMap持有载入的BeanDefinition的:
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,这个接口的实现完成BeanDefinition向容器注册的过程,过程不复杂,就是把解析到的BeanDefinition设置到hashMap中。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
//检查是否有相同名字的BeanDefinition已经在IOC容器中注册了,如果有,又不允许覆盖,则抛异常
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// 正常注册BeanDefinition的过程,放入Map中
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
完成了注册就是完成了IOC容器的初始化过程。