Spring容器的基本实现(二)-spring容器初始化的refesh(一)
上一篇,我们将到了构建标准环境,处理要加载配置文件的路径,如替换占位符等。
接下来我们讲容器初始化的refresh
回到看ClassPathXmlApplicationContext类,我们继续
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();//spring容器初始化的refresh
}
}
通过refresh(),进入到AbstractApplicationContext这个类上
/** 用于“刷新”和“销毁”的同步监视器 */
private final Object startupShutdownMonitor = new Object();//先记住这玩意
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {//一开始就是用了同步锁,把同步监视器锁起来了。
// Prepare this context for refreshing.
prepareRefresh();//准备此上下文以进行刷新,序号1
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//告诉子类刷新内部bean工厂。序号2
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);//准备bean工厂以在此上下文中使用。序号3
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);//允许在上下文子类中对bean工厂进行后处理。序号4
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);//在上下文中调用注册为bean的工厂处理器。序号5
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);//注册拦截bean创建的bean处理器。序号6
// Initialize message source for this context.
initMessageSource();//初始化此上下文的消息源。序号7
// Initialize event multicaster for this context.
initApplicationEventMulticaster();//初始化此上下文的事件广播器。序号8
// Initialize other special beans in specific context subclasses.
onRefresh();//在特定上下文子类中初始化其他特殊bean。序号9
// Check for listener beans and register them.
registerListeners();//检查监听器bean并注册它们。序号10
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);//实例化所有其余(非延迟初始化,非懒加载)单例。序号11
// 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();//重置Spring核心中的常见内核缓存,因为我们可能不再需要单例bean的元数据了...
}
}
}
==================================我是分割线===========================================
这里的代码里的任务比较多,我们一个一个来看。。
我们将任务清单一个个进行划分,命名序号,以便我们运行的时候会混乱。
序号1:prepareRefresh();//准备此上下文以进行刷新;
序号2:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//告诉子类刷新内部bean工厂。
序号3:prepareBeanFactory(beanFactory);//准备bean工厂以在此上下文中使用。
需要try起来的模块===================================
序号4:postProcessBeanFactory(beanFactory);//允许在上下文子类中对bean工厂进行后处理。
序号5:invokeBeanFactoryPostProcessors(beanFactory);//在上下文中调用注册为bean的工厂处理器。
序号6:registerBeanPostProcessors(beanFactory);//注册拦截bean创建的bean处理器。
序号7:initMessageSource();//初始化此上下文的消息源。
序号8:initApplicationEventMulticaster();//初始化此上下文的事件广播器。
序号9:onRefresh();//在特定上下文子类中初始化其他特殊bean。
序号10:registerListeners();//检查监听器bean并注册它们。
序号11:finishBeanFactoryInitialization(beanFactory);//实例化所有其余(非延迟初始化,非懒加载)单例。
序号12:finishRefresh();//最后一步:发布相应的事件。
catch 模块====================================
序号13:destroyBeans();//销毁已经创建的单例以避免资源动荡。
序号14:cancelRefresh(ex);//重置“有效”标志。
finally模块=======================================
序号15:resetCommonCaches();//重置Spring核心中的常见内核缓存,因为我们可能不再需要单例bean的元数据了…
首先是序号1:repareRefresh();//准备此上下文以进行刷新
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();//获取系统当前时间戳
this.closed.set(false);//表示这个上下文是否已经关闭的标志,false表示没关闭
this.active.set(true);//表示此上下文当前是否处于活动状态的标志,true为活动
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();//在上下文环境中初始化任何占位符属性源,这个好像没啥用,代码块是空的,有一句注释For subclasses: do nothing by default.默认情况下不做任何事情。
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
//这里做了两件事,
// 1、getEnvironment获取环境,如果没有,创建一个,有则返回
// 2、validateRequiredProperties定义一个缺少必要属性值异常,判断requiredProperties是否为空,为空就返回异常
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
//创建一个LinkedHashSet来装预发布的ApplicationEvents事件
this.earlyApplicationEvents = new LinkedHashSet<>();//允许收集前期的ApplicationEvents事件,一旦广播器可用,即可发布...
}
来看序号2:obtainFreshBeanFactory()
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()//告诉子类刷新内部bean工厂
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();//这个实现对这个上下文的底层进行实际的刷新bean工厂,关闭以前的bean工厂(如果有的话)和初始化一个新鲜的bean工厂,用于上下文生命周期的下一个阶段。
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
这里跟踪refreshBeanFactory()
进入AbstractRefreshableApplicationContext类
/**
* 这个实现对这个上下文的底层进行实际的刷新bean工厂,关闭以前的bean工厂(如果有的话)和初始化一个新鲜的bean工厂,用于上下文生命周期的下一个阶段。
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//实际上返回的是:this.beanFactory != null
destroyBeans();//销毁beans
closeBeanFactory();//关闭bean工厂
}
try {
//DefaultListableBeanFactory为ioc容器之源头,如同伊甸园般的存在
//为此上下文创建一个内部bean工厂。针对每个{@link #refresh()}尝试进行调用。
//createBeanFactory()如果它实现了ConfigurableApplicationContext,则返回父上下文的内部bean工厂; 否则,返回父上下文本身。
DefaultListableBeanFactory beanFactory = createBeanFactory();
//为序列化目的指定一个id,如果需要,可以将此BeanFactory从此id反序列化回BeanFactory对象。
beanFactory.setSerializationId(getId());
//自定义此上下文使用的内部bean工厂。 针对每个{@link #refresh()}尝试进行调用。
customizeBeanFactory(beanFactory);
//通过XmlBeanDefinitionReader加载bean定义。
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
createBeanFactory()中的内部实现
//为此上下文创建一个内部bean工厂。针对每个{@link #refresh()}尝试进行调用。
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
=====================getInternalParentBeanFactory()的相关代码====================
//如果它实现了ConfigurableApplicationContext,则返回父上下文的内部bean工厂; 否则,返回父上下文本身。
@Nullable
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}
beanFactory.setSerializationId(getId())的内部实现
/**
* 为序列化目的指定一个id,如果需要,可以将此BeanFactory从此id反序列化回BeanFactory对象。
*/
public void setSerializationId(@Nullable String serializationId) {
if (serializationId != null) {
//不为空执行在serializableFactories里加入一组值:键为serializationId,值为当前对象的弱引用
serializableFactories.put(serializationId, new WeakReference<>(this));
}
else if (this.serializationId != null) {
//从serializableFactories中移除键为当前对象属性serializationId的对象,也就是以前存入的当前对象的弱引用
serializableFactories.remove(this.serializationId);
}
//设置属性serializationId为输入参数
this.serializationId = serializationId;
}
customizeBeanFactory(beanFactory)内部实现
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
//设置给beanFactory对象相应属性,此属性的含义:是否允许覆盖同名称的不同定义的对象
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
//设置给beanFactory对象相应属性,此属性的含义:是否允许bean之间循环引用
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
loadBeanDefinitions(beanFactory);内部实现
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为给定的BeanFactory创建一个新的XmlBeanDefinitionReader。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 使用此上下文的资源加载环境配置bean定义阅读器。
//将环境设置为AbstractApplicationContext中的ConfigurableEnvironment.environment
//也就是初始创建的环境
beanDefinitionReader.setEnvironment(this.getEnvironment());
//设置ResourceLoader以用于资源位置。
//如果指定ResourcePatternResolver,则bean定义读取器将能够将资源模式解析为资源数组。
//默认是PathMatchingResourcePatternResolver,也可以通过ResourcePatternResolver接口解析资源模式。
//将其设置为{null}表明绝对路径资源加载
beanDefinitionReader.setResourceLoader(this);
//设置要用于解析的SAX实体解析器。
//创建解析器ResourceEntityResolver
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类提供读取的自定义初始化,然后继续实际加载bean定义。
initBeanDefinitionReader(beanDefinitionReader);
//用给定的XmlBeanDefinitionReader加载bean定义。
loadBeanDefinitions(beanDefinitionReader);
}
//为给定的BeanFactory创建一个新的XmlBeanDefinitionReader。
new XmlBeanDefinitionReader(beanFactory)详解
//BeanFactory以BeanDefinitionRegistry的形式加载bean定义
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
//super方法
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// 确定要使用的ResourceLoader。
if (this.registry instanceof ResourceLoader) {
//ResourceLoader用于加载资源的策略接口(例如类路径或文件系统资源)
//用于从类路径加载的伪URL前缀:“classpath:”
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
//路径匹配资源模式解析器,可以理解为通配符自动加载符合路径规则的文件。
//根据配置路径自动加载符合路径规则的xml文件、类文件等等
//比如:"classpath:*"
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// 尽可能继承环境
// 如果registry的类型不属于EnvironmentCapable,那么将构建一个新的标准环境
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
我们来看一下创建ResourceEntityResolver类的实现
//为指定的ResourceLoader(通常是一个ApplicationContext)创建一个ResourceEntityResolver。
//@param resourceLoader用于加载包含XML实体的ResourceLoader(或ApplicationContext)
public ResourceEntityResolver(ResourceLoader resourceLoader) {
super(resourceLoader.getClassLoader());
this.resourceLoader = resourceLoader;
}
super
/**
* 创建一个DelegatingEntityResolver委派给默认的BeansDtdResolver和PluggableSchemaResolver
*/
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
////用于从Spring类路径(或JAR文件)加载DTD
this.dtdResolver = new BeansDtdResolver();
//使用默认映射文件模式“META-INF / spring.schemas”加载模式URL - >模式文件位置映射。
//即XSD
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
这里需要讲一下关于xml的验证模式,关于xml文件的正确性,比较常用的验证模式有两种:DTD和XSD
DTD即文档类型定义,是一种xml约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。
可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。
//这里表示定义的DTD
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
我们的BeanFactoryTest.xml用得就是XSD
XML Schema语言就是XSD。XSD描述了XML的文档结构。
可以用一个指定的XSD来验证某个XML文档,以检查该XML文档是否符合其要求。
文档设计者可以通过XSD指定一个XML文档所允许的结构和内容,并可根据此检查一个XML文档是否有效。
XSD本身就是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它
//这里表示定义的XSD
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
在接下来就是initBeanDefinitionReader(beanDefinitionReader);
//这里就干了一件事,设置reader的验证模式,初始化用于加载此上下文的bean定义的bean定义reader器。
//this.validating设置是否使用XML验证。默认是true.
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
reader.setValidating(this.validating);
}
loadBeanDefinitions(beanDefinitionReader);内部实现
//用给定的XmlBeanDefinitionReader加载bean定义。
//bean工厂的生命周期由{@link #refreshBeanFactory}方法处理; 因此这个方法只是加载和/或注册bean定义。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();//获取配置资源,由于没有,所以是空
if (configResources != null) {
//空的跳过判断
reader.loadBeanDefinitions(configResources);
}
//此处获取我们之前给定的bean.xml配置文件的位置地址。
//断点里可以看到只有一个BeanFactoryTest.xml
String[] configLocations = getConfigLocations();//获取配置文件位置
if (configLocations != null) {
//进入加载或注册bean
reader.loadBeanDefinitions(configLocations);
}
}
深入reader.loadBeanDefinitions(configLocations);
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
//此处for循环加载数组中的配置文件位置
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
//返回条数
return counter;
}
继续深入loadBeanDefinitions(location);
//此处直接return返回了。我们再继续深入挖坑
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
深入挖坑loadBeanDefinitions(location, null);
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();//获取资源读取器
//如果资源读取器为空,返回BeanDefinitionStoreException异常
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//此处判断资源读取器的类型是通配符匹配器还是绝对路径加载器
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.资源模式匹配可用。
try {
//使用资源模式匹配读取资源,也就是读取bean信息
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 {
// Can only load single resources by absolute URL.只能通过绝对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;
}
}
在断点经过getResourceLoader();的时候,我们看一下ResourceLoader的值
至此,我们的spring以及预热装载了许多东西了。
环境属性以及环境资源
设置XML验证模式
设置资源配置文件读取器
设置配置文件封装
等等。。。。。
我们继续往下看。。
getResources(location);根据路径进行判断,如何加载配置文件,如:classpath:或classpath:*以及绝对路径加载
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//判断locationPattern是否以classpath*开头
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
//一个类路径资源(可能有多个同名的资源)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// 一个类路径资源模式
return findPathMatchingResources(locationPattern);
}
else {
// 具有给定名称的所有类路径资源
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// 通常只在此处的前缀后面查找模式,而在Tomcat上仅在“war:”协议的“* /”分隔符之后查找。
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// 一个文件模式
return findPathMatchingResources(locationPattern);
}
else {
// 具有给定名称的单个资源
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
由于我们是指定单个资源的位置,所以直接跳到最后一个读取
即:return new Resource[] {getResourceLoader().getResource(locationPattern)};
接下来继续分析
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//自定义协议解析,如果我们自定义了ProtocolResolver,是会优先调用的
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
//根据返回值是否为null判断自定义的ProtocolResolver是否解决了resource的加载
if (resource != null) {
return resource;
}
}
//根据location的开头判断是哪一种Resource,eg: http,classpath,file等
if (location.startsWith("/")) {//如果是'/'开头的
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//如果是classpath开头的,进入此处
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {//如果都不是,则尝试解析,如果实在解析不了,则解析为资源路径。
try {
// 尝试将网址解析为网址...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// 没有URL - >解析为资源路径。
return getResourceByPath(location);
}
}
}
此处条件都不匹配,直接进入catch模块,解析为资源路径。
getResourceByPath(location);
//返回给定路径中资源的资源句柄。默认实现支持类路径位置。 这应该适用于独立实现,但可以被覆盖,例如。 针对Servlet容器的实现。
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
/**
* ClassPathResource通过实现ContextResource接口来显式表示一个与上下文相关的路径。
*/
protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
super(path, classLoader);
}
@Override
public String getPathWithinContext() {
return getPath();
}
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
return new ClassPathContextResource(pathToUse, getClassLoader());
}
}
至此,读取器以及加载bean的信息以及完成,我们返回AbstractBeanDefinitionReader
接续思路:代码运行至int loadCount = loadBeanDefinitions(resources);//从指定的资源位置加载bean定义。
仅仅加载bean的定义,该位置也可以是位置匹配模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();//获取已经设置过的读取器
if (resourceLoader == null) {//判断读取器是否为空
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//判断读取器的类型
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
//通配符读取器
try {
//将给定的位置模式解析为Resource对象,又跑了回去,进行判断解析方式。如classpath或是classpath:*
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 {
// Can only load single resources by absolute 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(resources);//XML从指定的XML文件加载bean定义。
此处和上面不太一样,跑到XmlBeanDefinitionReader中,创建了一个对资源文件的编码进行处理的类
/**
* 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 {
return loadBeanDefinitions(new EncodedResource(resource));
}
继续往下loadBeanDefinitions(new EncodedResource(resource));
/**
* 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.isInfoEnabled()) {//判断日志是否启
//记录日志
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//内部创建了一个线程,创建ThreadLocal.ThreadLocalMap,是让同一个线程使用相同的currentResources
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
//将currentResources放入ThreadLocal中
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//将之前的封装好的resource添加到currentResources中,如果添加不进去,则抛出异常
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
//获取输入流信息
try {
//通过配置文件路径,创建输入流,encodedResource.resource.path=BeanFactoryTest.xml
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//创建XML实体的输入源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
//设置输入流的编码
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正工作在下面这句。。
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();//关闭输入流
}
}
catch (IOException ex) {//抛出io操作异常
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);//currentResources移除掉本次加载的encodedResource
if (currentResources.isEmpty()) {//如果currentResources为空
//那么移除当前正在加载的资源
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
我们深入进去看看doLoadBeanDefinitions(inputSource, encodedResource.getResource());
/**
* 实际上从指定的XML文件加载bean定义。
* 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 {
Document doc = doLoadDocument(inputSource, resource);//真正干活的方法,从指定的路径加载Document
return registerBeanDefinitions(doc, resource);//注册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);
}
}
跟踪Document doc = doLoadDocument(inputSource, resource)
/**
* Actually load the specified document using the configured DocumentLoader.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the DOM Document
* @throws Exception when thrown from the DocumentLoader
* @see #setDocumentLoader
* @see DocumentLoader#loadDocument
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
//inputSource,传入的XML实体的输入源
//getEntityResolver(),返回要使用的EntityResolver,如果未指定,则构建默认解析器。再次判断ResourceLoader
//this.errorHandler,异常处理器SimpleSaxErrorHandler
//getValidationModeForResource(resource),获取指定的{@link Resource}的验证模式
//isNamespaceAware(),是否要提供对XML名称空间的支持,目前是false
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
/**
* 这里贴一下getValidationModeForResource
* 获取指定的{@link Resource}的验证模式
*/
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
//嗯,我们没有得到明确的指示......让我们假设XSD,因为在检测停止之前(在找到文档的根标签之前)显然没有找到DTD声明。
return VALIDATION_XSD;
}
getEntityResolver()
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
//如果实体加载器不为空,使用指定的
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
//构建默认的实体加载器
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
这里讲一下EntityResolver
对于解析一个XML,SAX首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。EntityResolver的作用是项目本身可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程。
继续分析this.documentLoader.loadDocument()
/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//创建工厂,validationMode验证模式,是否要提供对XML名称空间的支持,目前是false
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
创建工厂createDocumentBuilderFactory(validationMode, namespaceAware);
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
` //创建新实例工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);//是否要提供对XML名称空间的支持,目前是false
//判断校验模式validationMode
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
//设置XSD强制识别名称空间...
factory.setNamespaceAware(true);
try {
//设置工厂属性
//SCHEMA_LANGUAGE_ATTRIBUTE;用于配置模式语言进行验证的JAXP属性。
//String类型值为:http://java.sun.com/xml/jaxp/properties/schemaLanguage
//XSD_SCHEMA_LANGUAGE;指示XSD模式语言的JAXP属性值。
//String类型值为:http://www.w3.org/2001/XMLSchema
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;//返回工厂
}
createDocumentBuilder(factory, entityResolver, errorHandler);
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {
//从XML文档获取DOM Document实例
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);//给docBuilder设置实体解析器
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);//设置异常处理
}
return docBuilder;//返回文档解析器对象
}
builder.parse(inputSource);
public Document parse(InputSource is) throws SAXException, IOException {
if (is == null) {
throw new IllegalArgumentException(
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"jaxp-null-input-source", null));
}
if (fSchemaValidator != null) {//判断架构验证是否为空
if (fSchemaValidationManager != null) {
fSchemaValidationManager.reset();//重置架构验证管理器
fUnparsedEntityHandler.reset();//重置实体处理器
}
resetSchemaValidator();//重置验证管理器
}
domParser.parse(is);//使用sun工具包,DOMParser真正执行解析xml文件,解析过程略。。
Document doc = domParser.getDocument();//获得解析后的DOM
domParser.dropDocumentReferences();//删除参考文档
return doc;//返回解析后的Document
}
返回doLoadDocument(inputSource, resource);//真正干活的方法,从指定的路径加载Document
此时已经结束了从指定路径加载Document,接下来一篇,我们讲Bean的注册