Spring IOC(二)容器初始化

时间:2021-08-13 00:31:13

本系列目录:

Spring IOC(一)概览

Spring IOC(二)容器初始化

Spring IOC(三)依赖注入

Spring IOC(四)总结

目录

一、ApplicationContext接口设计

二、深入源码,看IOC容器初始化

===========正文分割线===========

前面一篇概览了IOC容器的接口设计。

本文从ApplicationContext接口的一个实现类ClassPathXmlApplicationContext入手,分析容器初始化过程。先看一下ApplicationContext接口设计:

一、ApplicationContext接口设计

ApplicationContext是spring中较高级的容器。和BeanFactory类似,它可以加载配置文件中定义的bean,当有请求的时候分配bean。 另外,它增加了企业所需要的功能,比如,从属性文件解析文本信息和将事件传递给所指定的监听器。接口设计图如下:

Spring IOC(二)容器初始化

ApplicationContext继承5个接口:

1.2个核心接口:

ListableBeanFactory:支持获取bean 工厂的所有bean实例

HierarchicalBeanFactory:支持继承关系

2.3个拓展接口:

MessageSource:提供国际化支持

ApplicationEventPublisher:支持事件驱动模型中的事件发布器,这些事件和Bean的生命周期的结合为Bean的管理提供了便利。

ResourcePatternResolver:资源解析器

常见实现类:

1.FileSystemXmlApplicationContext:从指定文件地址的加载xml定义的bean

2.ClassPathXmlApplicationContext:从类路径下载入xml定义的bean

3.XmlWebApplicationContext:web 应用程序的范围内载入xml定义的bean

二、深入源码,看IOC容器初始化

为了方便理解和追踪代码,使用常用实现类ClassPathXmlApplicationContext写了一个小例子,步骤如下:

1).在类路径下新建xml,定义一个bean,其中daoImpl就是bean的名字,spring.aop.xml.dao.impl.DaoImpl对应具体的一个pojo.

<bean id="daoImpl" class="spring.aop.xml.dao.impl.DaoImpl" />

2).main方法中直接载入xml,然后获取bean,最后执行bean实例的方法。

     public static void main(String[] args) {
//源码入口,从类路径下读取xml
ApplicationContext ac1 = new ClassPathXmlApplicationContext("aop.xml");
Dao dao = (Dao)ac1.getBean("daoImpl");//根据名称获取Bean
dao.select();//执行Bean实例方法
}

下面我们就分析ClassPathXmlApplicationContext源码,来看看都做了什么。

2.1ClassPathXmlApplicationContext类图

Spring IOC(二)容器初始化

DefaultResourceLoader,该类设置classLoader,并且将配置文件 封装为Resource文件。

AbstractApplicationContext,该类完成了大部分的IOC容器初始化工作,同时也提供了扩展接口留给子类去重载。该类的refresh()函数是核心初始化操作。

AbstractRefreshableApplicationContext,该类支持刷新BeanFactory。

AbstractRefreshableConfigApplicationContext,该类保存了配置文件路径

AbstractXmlApplicationContext:该类支持解析bean定义文件

最后ClassPathXmlApplicationContext:只提供了一个简单的构造函数

Spring 将类职责分开,形成职责链,每一层次的扩展 都只是添加了某个功能

然后父类定义大量的模板,让子类实现,父类层层传递到子类 直到某个子类重载了抽象方法。这里应用到了职责链设计模式和模板设计模式,IOC是个容器工厂设计模式。

2.2 回顾上面的小例子,new ClassPathXmlApplicationContext("aop.xml");这行代码做了什么?

 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);//把ApplicationContext作为父容器,上述测试类中由于直接载入的xml,没有父容器所以实际传了null
setConfigLocations(configLocations);//替换${}后设置配置路径
if (refresh) {
refresh();//核心方法
}
}

ClassPathXmlApplicationContext的refresh()实际上就是调用了AbstractApplicationContext的refresh()方法。全方法被synchronized同步块锁住,源码如下:

 public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备刷新的上下文环境,例如对系统属性或者环境变量进行准备及验证。
prepareRefresh(); //启动子类的refreshBeanFactory方法.解析xml
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //为BeanFactory配置容器特性,例如类加载器、事件处理器等.
prepareBeanFactory(beanFactory); try {
//设置BeanFactory的后置处理. 空方法,留给子类拓展用。
postProcessBeanFactory(beanFactory); //调用BeanFactory的后处理器, 这些后处理器是在Bean定义中向容器注册的. 
invokeBeanFactoryPostProcessors(beanFactory); //注册Bean的后处理器, 在Bean创建过程中调用. 
registerBeanPostProcessors(beanFactory); //初始化上下文中的消息源,即不同语言的消息体进行国际化处理 
initMessageSource(); //初始化ApplicationEventMulticaster bean,应用事件广播器
initApplicationEventMulticaster(); //初始化其它特殊的Bean, 空方法,留给子类拓展用。
onRefresh(); //检查并向容器注册监听器Bean
registerListeners(); //实例化所有剩余的(non-lazy-init) 单例Bean.
finishBeanFactoryInitialization(beanFactory); //发布容器事件, 结束refresh过程. 
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} //销毁已经创建的单例Bean, 以避免资源占用.
destroyBeans(); //取消refresh操作, 重置active标志. 
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
//重置Spring的核心缓存
resetCommonCaches();
}
}
}

2.3 Resources定位

refresh方法中obtainFreshBeanFactory方法调用了refreshBeanFactory,该方法使用DefaultListableBeanFactory去定位resources资源

 protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {//创建并设置DefaultListableBeanFactory同时调用loadBeanDefinitions载入loadBeanDefinition
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);
}
}

loadBeanDefinitions其具体实现在AbstractXmlApplicationContext中,定义了一个Reader作为入参执行载入过程:

 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为给定的bean工厂创建一个reader
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);//核心方法
}
loadBeanDefinitions方法如下:
 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

getConfigResources采用模板方法设计模式,具体的实现由子类完成,实际上这里getConfigResources调用的就是子类ClassPathXmlApplicationContext的getConfigResources方法。ClassPathXmlApplicationContext继承了DefaultResourceLoader,具备了Resource加载资源的功能。至此完成了Resource定位!

2.4 BeanDefinition载入

这里支持2种模式:1.模板匹配多资源,生成Resource[]。2.载入单个资源url绝对地址,生成一个Resource

 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();//获取ResourceLoader资源加载器
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
// 1.匹配模板解析 ClassPathXmlApplicationContext是ResourcePatternResolver接口的实例
if (resourceLoader instanceof ResourcePatternResolver) { try {//接口ResourcePatternResolver
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 {
// 2.载入单个资源url绝对地址
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}

loadBeanDefinitions最终调用XmlBeanDefinitionReader.doLoadBeanDefinitions(),如下:

 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {// 取得xml文件的Document,解析过程是由DocumentLoader完成,默认为DefaultDocumentLoader
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);
}
}

registerBeanDefinitions是按照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的载入,BeanDefinition的载入分为两个部分,

1.调用xml解析器得到的document对象,但是这个对象并没有按照spring的bean规则进行解析。

2.DefaultBeanDefinitionDocumentReader的registerBeanDefinitions按照Spring的Bean规则进行解析。

2.5 BeanDefinition解析和注册

registerBeanDefinitions方法调用了doRegisterBeanDefinitions

 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.
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.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
} preProcessXml(root);
parseBeanDefinitions(root, this.delegate);// 从Document的根元素开始进行Bean定义的Document对象
postProcessXml(root); this.delegate = parent;
}
 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);
}
} 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)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
processBeanDefinition就是对bean标签的解析和注册
 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);// 1.解析
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);//代理去装饰:典型的装饰器模式
try {
// 2.向IOC容器注册Bean定义+bean工厂
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 3.触发注册事件: spring只提供了EmptyReaderEventListener空实现,如果需要你可以自定义
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

解析:parseBeanDefinitionElement方法就是具体的解析入口。解析elemnent->BeanDefinitionHolder,追踪parseBeanDefinitionElement:

 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);// 获取id
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);// 获取name List<String> aliases = new ArrayList<String>();// 获取别名
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);
} 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才是核心方法,追踪:

 public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
} try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}// 这里生成需要的BeanDefinition对象,为Bean定义信息的载入做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 1.解析<bean>元素属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));//2.解析description
//对各种BEAN元素信息进行解析
parseMetaElements(ele, bd);// 3.解析<meta>子元素
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//4.解析<lookup-method>子元素
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//5.解析<replaced-method>子元素 parseConstructorArgElements(ele, bd);//6.解析<constructor-arg>
parsePropertyElements(ele, bd);//7.解析<property>
parseQualifierElements(ele, bd);//8.解析<qualifier> 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;
}

经过这样逐层的分析,我们在xml文件中定义的BeanDefinition就被整个载入到IOC容器中,并在容器中建立了数据映射。这些数据结构可以以AbstractBeanDefinition为入口让IOC容器执行索引,查询和操作。

注册:registerBeanDefinition方法就是具体的注册入口。追踪registerBeanDefinition:

 public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());//向IoC容器注册BeanDefinition // 如果解析的BeanDefinition有别名, 向容器为其注册别名.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

registerBeanDefinition具体实现类:DefaultListableBeanFactory.registerBeanDefinition方法

 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; 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<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
} if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}

完成了BeanDefinition的注册,就完成了IOC容器的初始化过程。容器的作用就是对这些信息进行处理和维护,这些信息就是容器建立依赖反转的基础。

三、总结

本文先介绍ApplicationContext接口设计,再从其一个最常见实现类ClassPathXmlApplicationContext写了一个小例子,作为源码追踪的入口。

追踪了主要包括Resourse定位、BeanDefinition的载入、解析和注册3个模块。至此,容器初始化(Bean已生成)已完成,下一章我们看依赖注入的源码。