#描述:
1.从项目应用中的使用入口:
// 创建IoC容器,并进行初始化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
2.找到工厂实现类【ClassPathXmlApplicationContext】中对应的构造方法,执行ioc容器初始化:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
...............................
if (refresh) {
// 【核心方法】:该方法表示初始化(或者重建)ioc容器。即可以把原来的ApplicationContext销毁,重新执行初始化创建
refresh();
}
}
3.找到工厂抽象父类【AbstractApplicationContext】中的【refresh】方法:
3.1.该方法实现解析xml配置文件内容,封装成BeanDefinition对象,注册到BeanFactory中
3.2.该方法实现一些基础组件的注册:bean后置处理器组件、监听器组件、国际化资源组件
3.3.该方法实现bean对象的真正实例化。细节:初始化全部【singleton】单例对象,标记为【lazy-init】延迟加载的对象除外
/**
* Ioc 容器源码分析基础案例
*/
@Test
public void testIoC() {
// ApplicationContext是容器的高级接口,BeanFacotry(顶级容器/根容器,规范了/定义了容器的基础行为)
// Spring应用上下文,官方称之为 IoC容器(错误的认识:容器就是map而已;准确来说,map是ioc容器的一个成员,
// 叫做单例池, singletonObjects,容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程)
/**
* Ioc容器创建管理Bean对象的,Spring Bean是有生命周期的
* 构造器执行、初始化方法执行、Bean后置处理器的before/after方法、:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
* Bean工厂后置处理器初始化、方法执行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
* Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
LagouBean lagouBean = applicationContext.getBean(LagouBean.class);
System.out.println(lagouBean);
}
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { // 资源配置文件成员变量,是一个数组,支持多个spring的配置文件
@Nullable
private Resource[] configResources;
// 默认构造方法
public ClassPathXmlApplicationContext() {
}
// 如果已经存在一个ioc容器,可以在构造的时候设置【父】容器
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
// 【重点跟踪】根据xxx.xml配置文件,创建ioc容器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
..........................................
/**
*【重点跟踪】方法说明:
* 根据xml文件的定义,以及父容器,创建一个新的ClassPathXmlApplicationContext
*
*参数说明:
* configLocations:xml配置文件数组
* refresh:是否要重新创建ioc容器。加载全部bean的定义和创建所有的单例对象
* parent:父容器
*/
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);// 设置父容器
// 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();// 【核心方法】:该方法表示初始化(或者重建)ioc容器。即可以把原来的ApplicationContext销毁,重新执行初始化创建
}
}
..........................................
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
..........................................
/**
* 【重点跟踪】方法说明:
* 【核心方法】:该方法表示初始化(或者重建)ioc容器。即可以把原来的ApplicationContext销毁,重新执行初始化创建
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
// 创建ioc容器,同步加锁,保障线程安全
synchronized (this.startupShutdownMonitor) {
/**
* 1.刷新预处理
* 准备工作:记录容器启动的时间,和状态标记
*/
prepareRefresh();
/**
* 2.阅读XML中的信息,注册BeanDefinition(重点)
* obtainFreshBeanFactory 获取 beanFactory 实例并且完成BeanDefinition的注册工作
* // 关键步骤:
* // 1.根据配置文件中配置内容,解析成一个个Bean实例(BeanDefinition)
* // 2.将一个个Bean实例,注册到BeanFactory中
* // 3.细节:这里的Bean实例仅仅是描述Bean的相关信息,此时还没有真正创建对应的bean对象
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 3.beanFactory 预装工作,
* // 设置BeanFactory:
* // 1.设置类加载器
* // 2.设置BeanPostProcessor(bean后置处理器)
* // 3.注册特殊的bean(框架内部使用的bean)
*/
prepareBeanFactory(beanFactory);
try {
// 4.设置BeanFactoryPostProcessor
postProcessBeanFactory(beanFactory);
/**
* 5.执行bean后场处理器,执行完毕
* 解析BeanDefinition对象,put到map里
* 再次执行bean后置工厂处理器
*/
invokeBeanFactoryPostProcessors(beanFactory);
// 6.注册BeanPostProcessor的实现类:
// 1.该接口有两个方法:
// postProcessBeforeInitialization(),在init-method属性指定的方法前调用
// postProcessAfterInitialization(),在init-method属性指定的方法后调用
registerBeanPostProcessors(beanFactory);
// 7.初始化国际化支持的资源文件
initMessageSource();
// 8.初始化ApplicationContext事件广播器
initApplicationEventMulticaster();
// 9.模板方法:用于特殊bean的初始化,默认是空实现
// (在api中如果预留了一些方法实现是空,表示该方法是留给子类自我实现。那么这些方法称为:钩子方法)
onRefresh();
// 10.注册事件监听器:监听器需要实现ApplicationListener接口
registerListeners();
// 11.创建单实例业务Bean(非懒加载单例bean)
// 服务启动的时候,就会初始化所有的单例bean
finishBeanFactoryInitialization(beanFactory);
// 【最后一步】:
// 12.发布广播事件。ApplicationContext初始化完成
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 如果发生异常,需要销毁已经创建的singleton对象
destroyBeans();
// 将active状态设置为false
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();
}
}
}
}
#主体流程小结:
1.在应用程序中,通过调用ClassPathXmlApplicationContext工厂实现类构造方法,初始化创建ioc容器入口
2.在ClassPathXmlApplicationContext构造方法中,调用refresh方法:
2.1.方法名称很特别,不是init,而是refresh。refresh表示不仅仅是初始化创建ioc容器,还有可能是已经有了一个ioc容器,需要更新的意思
2.2.spring框架在处理过程中,会考虑先释放已经存在的ioc容器,再重新创建一个新的ioc容器
3.spring框架允许在一个应用中,可以存在多个ioc容器,并且可以建立它们之间的父子关系。比如在ssm框架整合中,就有两个ioc容器:
3.1.通过ContextLoaderListener监听器,加载spring配置文件,创建的父容器
3.2.通过DispatcherServlet前端控制器,加载springmvc主配置文件,创建的子容器
4.spring框架在创建ioc容器时,主体流程:
4.1.设置容器的初始化状态。比如容器的启动时间,容器的激活状态
4.2.【重点】解析bean.xml配置文件,将xml配置文件中的配置信息,比如<bean id="" class=""/>标签的配置信息,解析封装成BeanDefinition对象
4.3.将BeanDefinition对象,注册到BeanFactory容器中。需要注意:此时还没有创建真正的bean对象,只是解析封装xml配置文件内容
4.4.设置一些公共资源。比如bean的后置处理器、类加载器、监听器、国际化资源等
4.5.【重点】根据BeanDefinition对象,真正创建bean对象。需要注意:此时创建的是全部单例【singleton】、且不是延迟加载【lazy-init】的对象
4.6.最后一步广播事件,进行善后处理
创建Bean容器,注册bean对象
#描述流程:
1.调用【AbstractApplicationContext】refresh()方法:
初始化创建Bean容器入口
2.调用【AbstractApplicationContext】obtainFreshBeanFactory()方法
3.调用【AbstractRefreshableApplicationContext】refreshBeanFactory()方法:
创建Bean容器,加载并注册bean
4.调用【AbstractRefreshableApplicationContext】customizeBeanFactory()方法:
4.1.设置bean覆盖。如果在配置文件中通过bean标签配置时,有id或者name属性值相同的bean标签配置。spring框架默认在同一个配置文件中出现重复,则报错。在不同配置文件中出现,则进行覆盖
4.2.设置bean循环引用。循环引用是指:A依赖B、B依赖C、C依赖A
5.调用【AbstractXmlApplicationContext】loadBeanDefinitions()方法:
加载解析xxx.xml配置文件,根据xml配置文件内容,将bean标签的定义,解析成BeanDefinition实例。并且注册到beanFactory中
6.调用【AbstractRefreshableApplicationContext】getBeanFactory()方法:
返回Bean容器
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
/**
* 刷新或创建BeanFactory
*/
refreshBeanFactory();
/**
* 将刚生成的BeanFactory返回
*/
return getBeanFactory();
}
/**
* 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.
* 方法说明:
* 1.创建Bean容器,加载并且注册bean
* 2.首先关闭上一次创建的bean factory,如果还存在的话
* 3.初始化创建一个新的bean factory
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
// 判断是否存在bean factory,如果存在需要执行释放操作
if (hasBeanFactory()) {
// 判断是否存在bean factory,如果存在需要执行释放操作
destroyBeans();
// 销毁当前Bean容器
closeBeanFactory();
}
try {
// 实例化DefaultListableBeanFactory 创建一个新的Bean容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置序列化id
beanFactory.setSerializationId(getId());
//自定义bean工厂的一些属性(允许覆盖和循环依赖)
// 【重点跟踪】定制化设置beanFactory
// 1.设置bean覆盖。如果在配置文件中通过bean标签配置时,有id或者name属性值相同的bean标签配置。
// spring框架默认在同一个配置文件中出现重复,则报错。在不同配置文件中出现,则进行覆盖
// 2.设置bean循环引用。循环引用是指:
// A依赖B、B依赖C、C依赖A
customizeBeanFactory(beanFactory);
// 【重点跟踪】加载解析xxx.xml配置文件,根据xml配置文件内容,将bean标签的定义,
// 解析成BeanDefinition实例。并且注册到beanFactory中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
//赋值当前的beanFactory
this.beanFactory = beanFactory;
}
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
/**
*定制化设置beanFactory
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
// 1.设置bean覆盖。如果在配置文件中通过bean标签配置时,有id或者name属性值相同的bean标签配置。
// spring框架默认在同一个配置文件中出现重复,则报错。在不同配置文件中出现,则进行覆盖
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
// 2.设置bean循环引用。循环引用是指:
// A依赖B、B依赖C、C依赖A
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*方法说明:
* 通过xml配置文件,读取解析内容,封装到BeanDefinition中
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建XMLBeanDefinition阅读器
// 注解的是AnnotatedBeanDefinitionReader
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);
// 加载并注册BeanDefinition信息集合
loadBeanDefinitions(beanDefinitionReader);
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
第十步:
解析xml配置文件流程
#流程描述:
1.调用【AbstractBeanDefinitionReader】loadBeanDefinitions方法:
解析xml文档入口
2.调用【XmlBeanDefinitionReader】loadBeanDefinitions方法:
加载xml配置文件内容,获取InputStream流对象
3.调用【XmlBeanDefinitionReader】doLoadBeanDefinitions方法:
根据InputStream流,获取Document文档对象
4.调用【DefaultDocumentLoader】loadDocument方法:
工具类,通过DocumentBuilder构建器,将InputStream流,转换成Document文档对象
5.调用【XmlBeanDefinitionReader】registerBeanDefinitions方法:
根据Document文档对象,完成解析成BeanDefinition对象
6.调用【DefaultBeanDefinitionDocumentReader】registerBeanDefinitions方法:
从Document文档根元素开始解析,转换成BeanDefinition对象
7.调用【DefaultBeanDefinitionDocumentReader】doRegisterBeanDefinitions方法:
从根元素root开始解析,注册每一个BeanDefinition对象
8.调用【DefaultBeanDefinitionDocumentReader】parseBeanDefinitions方法:
解析beans根标签的的具体子标签。比如:bean/alias/import
9.调用【DefaultBeanDefinitionDocumentReader】parseDefaultElement方法:
私有方法,解析beans标签的具体子元素标签。比如<bean/>、<import/>等
10.调用【BeanDefinitionParserDelegate】parseBeanDefinitionElement方法:
解析<bean id="" class=""/>标签。转换成BeanDefinition对象
11.调用【DefaultBeanDefinitionDocumentReader】processBeanDefinition方法:
将BeanDefinition对象,注册到BeanFactory中
12.调用【BeanDefinitionReaderUtils】registerBeanDefinition方法:
将BeanDefinition对象,注册到BeanDefinitionRegistry中。此处的BeanDefinitionRegistry就是BeanFactory容器
#描述:
通过完成xml配置文件解析,将xml中的<bean>标签配置内容,转换成BeanDefinition对象,并且注册到BeanFactory中。关于解析xml和BeanDefinition对象,主要有以下api:
BeanDefinition:
存储Bean相关信息。比如bean对应的类、是否是单例、是否是延迟加载等
XmlBeanDefinitionReader:
加载读取xml配置文件,获取xml对应的Document文档对象
DefaultBeanDefinitionDocumentReader:
根据Document文档对象,从根元素开始解析子标签配置(重点是bean标签),转换成对应的BeanDefinition对象,注册到BeanFactory
BeanDefinitionParserDelegate:
spring的xml配置文件内容,以及BeanDefinition委派类。是人大代表。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* 单例作用范围
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* 多例作用范围
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
.................................................
/**
* 设置bean类名称
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* 获取bean类名称
*/
@Nullable
String getBeanClassName();
/**
* 设置bean作用范围
*/
void setScope(@Nullable String scope);
/**
* 获取bean作用范围
*/
@Nullable
String getScope();
/**
* 设置延迟加载
*/
void setLazyInit(boolean lazyInit);
/**
* 获取bean是否延迟加载
*/
boolean isLazyInit();
/**
* 设置bean的依赖(在bean标签中的depend-on属性)
*/
void setDependsOn(@Nullable String... dependsOn);
/**
* 获取bean的依赖
*/
@Nullable
String[] getDependsOn();
.................................................
/**
* 对于通过工厂方法实例化的bean。设置工厂名称
*/
void setFactoryBeanName(@Nullable String factoryBeanName);
/**
*对于通过工厂方法实例化的bean。获取工厂名称
*/
@Nullable
String getFactoryBeanName();
/**
* 对于通过工厂方法实例化的bean。设置工厂方法名称
*/
void setFactoryMethodName(@Nullable String factoryMethodName);
/**
* 对于通过工厂方法实例化的bean。获取工厂方法名称
*/
@Nullable
String getFactoryMethodName();
/**
* 获取构造方法参数(构造方法注入)
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* set方法注入
*/
MutablePropertyValues getPropertyValues();
/**
* 是否是单例
*/
boolean isSingleton();
/**
* 是否是多例
*/
boolean isPrototype();
.................................................
}
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
// 嵌套的beans标签
public static final String NESTED_BEANS_ELEMENT = "beans";
// alias标签
public static final String ALIAS_ELEMENT = "alias";
// name属性
public static final String NAME_ATTRIBUTE = "name";
// alias属性
public static final String ALIAS_ATTRIBUTE = "alias";
// import标签
public static final String IMPORT_ELEMENT = "import";
// resource属性
public static final String RESOURCE_ATTRIBUTE = "resource";
// profile属性
public static final String PROFILE_ATTRIBUTE = "profile";
..........................................
}
public class BeanDefinitionParserDelegate {
public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
/**
* Value of a T/F attribute that represents true.
* Anything else represents false. Case seNsItive.
*/
public static final String TRUE_VALUE = "true";
public static final String FALSE_VALUE = "false";
public static final String DEFAULT_VALUE = "default";
public static final String DESCRIPTION_ELEMENT = "description";
public static final String AUTOWIRE_NO_VALUE = "no";
public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
public static final String NAME_ATTRIBUTE = "name";
public static final String BEAN_ELEMENT = "bean";
public static final String META_ELEMENT = "meta";
public static final String ID_ATTRIBUTE = "id";
public static final String PARENT_ATTRIBUTE = "parent";
public static final String CLASS_ATTRIBUTE = "class";
public static final String ABSTRACT_ATTRIBUTE = "abstract";
public static final String SCOPE_ATTRIBUTE = "scope";
private static final String SINGLETON_ATTRIBUTE = "singleton";
public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
public static final String AUTOWIRE_ATTRIBUTE = "autowire";
public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
public static final String PRIMARY_ATTRIBUTE = "primary";
public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
public static final String INDEX_ATTRIBUTE = "index";
public static final String TYPE_ATTRIBUTE = "type";
public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
public static final String KEY_TYPE_ATTRIBUTE = "key-type";
public static final String PROPERTY_ELEMENT = "property";
public static final String REF_ATTRIBUTE = "ref";
public static final String VALUE_ATTRIBUTE = "value";
public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
public static final String REPLACER_ATTRIBUTE = "replacer";
public static final String ARG_TYPE_ELEMENT = "arg-type";
public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
public static final String REF_ELEMENT = "ref";
public static final String IDREF_ELEMENT = "idref";
public static final String BEAN_REF_ATTRIBUTE = "bean";
public static final String PARENT_REF_ATTRIBUTE = "parent";
public static final String VALUE_ELEMENT = "value";
public static final String NULL_ELEMENT = "null";
public static final String ARRAY_ELEMENT = "array";
public static final String LIST_ELEMENT = "list";
public static final String SET_ELEMENT = "set";
public static final String MAP_ELEMENT = "map";
public static final String ENTRY_ELEMENT = "entry";
public static final String KEY_ELEMENT = "key";
public static final String KEY_ATTRIBUTE = "key";
public static final String KEY_REF_ATTRIBUTE = "key-ref";
public static final String VALUE_REF_ATTRIBUTE = "value-ref";
public static final String PROPS_ELEMENT = "props";
public static final String PROP_ELEMENT = "prop";
public static final String MERGE_ATTRIBUTE = "merge";
public static final String QUALIFIER_ELEMENT = "qualifier";
public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";
................................................
}
/**
*方法说明:
* 加载xml配置文件,转换BeanDefinition对象,注册到BeanFactory中。【入口方法】
*/
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
//【重点跟踪】
counter += loadBeanDefinitions(location);
}
return counter;
}
/**
* 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
* *方法说明:
* * 加载xml配置文件内容,获取InputStream流对象
*/
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);
}
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 {
// 将资源文件转为InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 从InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
// 设置字符集编码
inputSource.setEncoding(encodedResource.getEncoding());
}
//具体的读取过程 真正加载xml配置文件,转换成Document对象
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
// 释放io流资源
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();
}
}
}
/**
* 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
* 方法说明:
* * 根据InputStream流,获取Document文档对象
*/
//此方法和5.0.7版本有差别
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 通过java自带的dom解析工具加载解析XML文件,最终形成Document对象
Document doc = doLoadDocument(inputSource, resource);
//解析document对象,封装BeanDefinition对象并进行注册
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);
}
}
/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
* *方法说明:
* * 工具类,通过DocumentBuilder构建器,将InputStream流,转换成Document文档对象
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 通过DocumentBuilder构建器,解析xml配置文件内容,获取Document文档对象
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
/**
* 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
* *方法说明:
* * 根据Document文档对象,完成解析成BeanDefinition对象
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader来解析Document对象,完成BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获得容器中已经注册的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,BeanDefinitionDocumentReader只是个接口,具体的实现过程在DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 统计新的的BeanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* Opens a DOM Document; then initializes the default settings
* specified at the {@code } level; then parses the contained bean definitions.
* *方法说明:
* * 从Document文档根元素开始解析,转换成BeanDefinition对象
*/
//此处和5.0.7版本有差别
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 真正实现BeanDefinition解析和注册工作
doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
* Register each bean definition within the given root {@code } element.
* *方法说明:
* * 从根元素root开始解析,注册每一个BeanDefinition对象
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// Any nested elements will cause recursion in this method. In
// order to propagate and preserve 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.
// 人大代表:代表spring配置文件中的标签和属性、以及最终转换成的BeanDefinition对象
// 这里使用了委托模式,将具体的BeanDefinition解析工作交给了BeanDefinitionParserDelegate去完成
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 此段代码不需要关心
// 判断该根标签是否包含http://www.springframework.org/schema/beans默认命名空间
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);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
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;
}
}
}
// 留给子类实现的钩子方法。此时不需要关心 在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
// 【重点跟踪方法】:从根元素开始,解析配置文件内容
// 委托给BeanDefinitionParserDelegate,从Document的根元素开始进行BeanDefinition的解析
parseBeanDefinitions(root, this.delegate);
// 留给子类实现的钩子方法。此时不需要关心 在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
this.delegate = parent;
}
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
*
* @param root the DOM root element of the document
* 方法说明:
* 解析beans根标签的的具体子标签。比如:bean/alias/import
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 加载的Document对象是否使用了Spring默认的XML命名空间(beans命名空间)
if (delegate.isDefaultNamespace(root)) {
// 获取Document对象根元素的所有子节点(bean标签、import标签、alias标签和其他自定义标签context、aop等)
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
// bean标签、import标签、alias标签,则使用默认解析规则
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
} else {
//像context标签、aop标签、tx标签,则使用用户自定义的解析规则解析元素节点
delegate.parseCustomElement(ele);
}
}
}
} else {
// 如果不是默认的命名空间,则使用用户自定义的解析规则解析元素节点,此时不需要关心
delegate.parseCustomElement(root);
}
}
/**
*方法说明:
* 私有方法,解析beans标签的具体子元素标签
*/
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);
}
}
/**
* Parses the supplied {@code } element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
* *方法说明:
* * 1.解析 标签配置
* * 2.转换成BeanDefinition对象
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取bean的id
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取bean的name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 别名处理,此时不需要关心
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// 将id属性设置为bean的名称
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的id或者name是否唯一
checkNameUniqueness(beanName, aliases, ele);
}
//【重点方法】解析bean标签,封装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);
// 【重点方法】创建BeanDefinitionHolder对象并返回。这里的BeanDefinitionHolder代表
// 将BeanDefinition对象和BeanName封装到BeanDefinitionHolder对象中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
* *方法说明:
* * 1.解析 标签配置
* * 2.转换成BeanDefinition对象
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析标签,获取BeanDefinition
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果需要,则装饰BeanDefinition对象
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 注册最终的BeanDefinition到BeanDefinitionRegistry(DefaultListableBeanFactory)
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));
}
}
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*方法说明:
* 1.将BeanDefinition对象,注册到BeanDefinitionRegistry中
* 2.这里的BeanDefinitionRegistry,其实就是DefaultListableBeanFactory,也就是BeanFactory
* 容器
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
// 将BeanDefinition对象,注册到BeanFactory中。它是一个Map。
// key==beanName;value=BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
#小结:
1.解析bean.xml配置文件,获取配置文件内。
比如:<bean id="customerService" class="com.itheima.service.impl.CustomerServiceImpl"/>
2.将配置文件内容中,重点关注bean标签的配置。将bean标签的配置信息,封装成BeanDefinition对象
3.将BeanDefinition对象,注册到BeanFactory容器中
4.需要注意,到此处仅仅只是封装了xml配置文件信息到BeanDefinition中,还没有真正创建相关的bean对象
5.在实现过程中,spring框架提供了一系列的api和api方法,实现过程比较复杂:
这里大家可能会有一个疑问,不就是解析xml配置文件吗?为什么要这么复杂呢?
6.答案是:
6.1.spring框架需要尽可能的通用,一定的复杂度是必然的
6.2.spring框架设计多api,是为了满足模块化,组件式开发。将各个组件模块之间进行解耦,在我们的项目中,用到哪一个模块,就只需要引入哪一个模块
6.3.spring框架设计多api方法,是让一个方法只做一件事情,满足【单一职责】的设计原则。目的还是为了解耦