共计 13333 个字符,预计需要花费 34 分钟才能阅读完成。
1. 类的层级图
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractXmlApplicationContext
- ClassPathXmlApplicationContext
2. spring 容器启动入口,执行胜利容器就启动实现了
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
3. 进入 ClassPathXmlApplicationContext 类的构造方法
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {super(parent);
// 初始化配置文件 xml 的地位,解析 configLocations,即.xml 文件
// 办法实现所在位置:父类 AbstractRefreshableConfigApplicationContext
setConfigLocations(configLocations);
if (refresh) {
//spring 容器启动的主流程(***** 重要 *****)// 办法实现所在位置:父类 AbstractApplicationContext
refresh();}
}
// 此办法所在类:AbstractRefreshableConfigApplicationContext
public void setConfigLocations(@Nullable String... locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");
// 将 xml 文件维护在 configLocations 属性上
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
//resolvePath 办法波及含糊匹配,先不看
this.configLocations[i] = resolvePath(locations[i]).trim();}
}
else {this.configLocations = null;}
}
4. spring 容器启动的外围办法 refresh();
/**
* 该办法是 spring 容器初始化的外围办法。* 是 spring 容器初始化的外围流程,是一个典型的父类模板设计模式的使用
* 依据不同的上下文对象,会掉到不同的上下文对象子类办法中
*
* 外围上下文子类有:* ClassPathXmlApplicationContext
* FileSystemXmlApplicationContext
* AnnotationConfigApplicationContext
* EmbeddedWebApplicationContext(springboot)
*/
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 为容器初始化做筹备,能够不看
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/*
* 1、创立 BeanFactory 对象
* 2、xml 解析
* 传统标签解析:bean、import 等
* 自定义标签解析:例如:<context:component-scan base-package="com.xiangxue.jack"/>
* 自定义标签解析流程:* a、依据以后解析标签的头信息找到对应的 namespaceUri
* b、加载 spring 所以 jar 中的 spring.handlers 文件。并建设映射关系
* c、依据 namespaceUri 从映射关系中找到对应的实现了 NamespaceHandler 接口的类
* d、调用类的 init 办法,init 办法是注册了各种自定义标签的解析类
* e、依据 namespaceUri 找到对应的解析类,而后调用 paser 办法实现标签解析
* 3、把解析进去的 xml 标签信息封装成 BeanDefinition 对象
*/
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);
// Invoke factory processors registered as beans in the context.
// 注册 beanFactoryPostProcessor 对象
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册 beanPostProcessor 实例,在 bean 创立的时候实现拦挡
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 国际化,重要水平 2
initMessageSource();
// Initialize event multicaster for this context.
// 初始化工夫治理类
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 此办法着重了解模板设计模式,因为在 springboot 中,这个办法是用来做内嵌 tomcat 启动的
onRefresh();
// Check for listener beans and register them.
// 往工夫治理类中注册事件类
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/*
* 在之前曾经实例化了 BeanFactoryPostProcessor 以及 beanPostProcessor
* 上面开始实例化剩下的所有非懒加载的单例对象
*/
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();}
}
}
5. obtainFreshBeanFactory() 辨析
// 父类 AbstractApplicationContext 办法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 外围办法,必须读,重要水平:5
// 此办法是钩子办法,在子类中实现
refreshBeanFactory();
return getBeanFactory();}
6. refreshBeanFactory() 辨析
//AbstractRefreshableApplicationContext(继承 AbstractApplicationContext)
protected final void refreshBeanFactory() throws BeansException {
// 如果 BeanFactory 不为空,则革除 BeanFactory 和外面的实例
if (hasBeanFactory()) {destroyBeans();
closeBeanFactory();}
try {
// 创立 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 设置是否能够循环依赖 allowCircularReferences
// 是否容许应用雷同名称从新注册不同的 bean 实现.
customizeBeanFactory(beanFactory);
// 解析 xml,并把 xml 中的标签封装成 BeanDefinition 对象,调用子类办法
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}
}
catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for" + getDisplayName(), ex);
}
}
7. loadBeanDefinitions() 辨析
//AbstractXmlApplicationContext 类(继承 AbstractRefreshableConfigApplicationContext)protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创立 xml 的解析器,这里是一个委托模式
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 这里传一个 this 进去,因为 ApplicationContext 是实现了 ResourceLoader 接口的
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);
// 次要看这个办法 重要水平 5
loadBeanDefinitions(beanDefinitionReader);
}
// 以后类
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();
if (configResources != null) {reader.loadBeanDefinitions(configResources);
}
// 获取须要加载的 xml 配置文件
// 在第一步 classPathXmlApplicationContext 结构器中,曾经初始化了 configLocation
String[] configLocations = getConfigLocations();
if (configLocations != null) {reader.loadBeanDefinitions(configLocations);
}
}
8. 委托给 reader 来解析 reader.loadBeanDefinitions()
//AbstractBeanDefinitionReader 类中办法
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;
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
// 调用以后类的办法
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {// 之前 AbstractXmlApplicationContext 类中 loadBeanDefinitions() 办法中
//beanDefinitionReader.setResourceLoader(this); 传入的 this,用于此处拿到上下文对象
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 文件门路,形如:classpath*:user/**/*-context.xml, 转换成 Resource 对象类型,其实就是用流
// 的形式加载配置文件,而后封装成 Resource 对象,不重要,能够不看
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 次要看这个办法 ** 重要水平 5
// 调用以后类的办法 - 重载
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 仅仅可能加载单个的绝对路径的 xml 配置文件
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {actualResources.add(resource);
}
if (logger.isTraceEnabled()) {logger.trace("Loaded" + count + "bean definitions from location [" + location + "]");
}
return count;
}
}
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
// 模板设计模式,调用到子类中的办法
count += loadBeanDefinitions(resource);
}
return count;
}
9. EncodedResource 带编码的对 Resource 对象的封装,将 inputSource 对象封装成 Documet 对象
// 子类 XmlBeanDefinitionReader 中的办法(继承 AbstractBeanDefinitionReader)// 对接口 BeanDefinitionReader 的实现
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//EncodedResource 带编码的对 Resource 对象的封装
return loadBeanDefinitions(new EncodedResource(resource));
}
// 从资源 Resource 中拿到输出流 InputStream,保护到 InputSource 中,而后调用 doLoaderBeanDefinitions 解析
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 {
// 获取 Resource 对象中的 xml 文件流对象
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//InputSource 是 jdk 中的 sax xml 文件解析对象
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());
}
// 次要看这个办法 ** 重要水平 5- ***** 加载 beanDefinition*****
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();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 把 inputSource 封装成 Document 文件对象,这是 jdk 的 API
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;
}
// 异样此处略去......
}
10. 解析为 document 对象,之后就要注册 beanDefinition 了
- 在 spring 的加载过程中,BeanDefinition 是一个重要的数据结构,它是在创建对象之前,对象数据的一种存在模式
- xml —— beanDefinition ——bean 从 xml 配置 bean,到解析 xml 创立 beanDefinition,到从 beanDefinition 实例为 bean 对象,这是一个流程。
//XmlBeanDefinitionReader 类
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 又来一记委托模式,BeanDefinitionDocumentReader 委托这个类进行 document 的解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 次要看这个办法,createReaderContext(resource) XmlReaderContext 上下文,封装了 XmlBeanDefinitionReader 对象
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
11. 通过上一个委托进行注册 beanDefinition
- spring 将 xml 文件封装成了 Document 对象,而后委托给 BeanDefinitionDocumentReader 来解析
//DefaultBeanDefinitionDocumentReader(实现 BeanDefinitionDocumentReader 接口)public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 次要看这个办法,把 root 节点传进去
// 注册 beanDefinition,将 document 中 root 元素传入
doRegisterBeanDefinitions(doc.getDocumentElement());
}
// 委托给 document 的解析器,入参为 document 的根元素,就是 spring-context.xml 的 beans 元素:protected void doRegisterBeanDefinitions(Element root) {
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);
// 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;
}
}
}
// 冗余设计,这里有两个钩子办法,典型的模板设计,由子类去实现
preProcessXml(root);
// 次要看这个办法,标签具体解析过程
// 具体的解析 document 对象,注册 beanDefinition 的逻辑在这里实现
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 判断根元素的命名空间是否为空或者是 xmlns="http://www.springframework.org/schema/beans"
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)) {
// 默认标签解析,bean,import 等
parseDefaultElement(ele, delegate);
}
else {
// 自定义标签解析, context 等
// a、依据以后解析标签的头信息找到对应的 namespaceUri
// b、加载 spring 所以 jar 中的 spring.handlers 文件。并建设映射关系
// c、依据 namespaceUri 从映射关系中找到对应的实现了 NamespaceHandler 接口的类
// d、调用类的 init 办法,init 办法是注册了各种自定义标签的解析类
// e、依据 namespaceUri 找到对应的解析类,而后调用 paser 办法实现标签解析
delegate.parseCustomElement(ele);//*********** 重点
}
}
}
}
else {delegate.parseCustomElement(root);
}
}
总结:
- 上述是启动 spring 流程的第一步,解析配置文件,当然咱们这里是以 xml 配置的形式剖析。也可能是注解配置的办法,后续再来剖析注解形式。
- 创立 applicationContext 对象,将 xml 文件的门路保护到 AbstractRefreshableApplicationContext 的属性上
- refresh 启动 spring 流程,这里是 spring 启动的外围流程
- 第一步 obtainBeanFactory,这这个办法里,会创立 bean 工厂,加载 xml 文件,委托给 XmlBeanDefinitionReader 解析
- XmlBeanDefinitionReader 将 xml 字符串门路封装为 Resource 对象,再转为 InputStream 流,最初把输出流生成 Document 对象,而后委托给 BeanDefinitionDocumentReader 解析。
正文完