ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext
用了这么久的框架,是时候搞一下源码了,一般最初接触spring 从以下步骤开始
- 创建一个bean类 并创建
ooxx.xml
之类的spring bean描述文件
<?xml version="1.0" encoding="UTF-8"?>
<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">
<bean id="beanEntity" class="cn.felord.spring.bean.SpringBean">
<property name="beanName" value="springBean"/>
</bean>
</beans>
- 加载器进行加载 获取bean
ApplicationContext context = new FileSystemXmlApplicationContext("F:\\workbase\\spring-framework-source\\src\\main\\resources\\bean.xml");
// ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
BeanEntity bean = context.getBean(BeanEntity.class);
System.out.println("beanName >>>>>>>>>>>>>>>>>>>>>>>>>> "+bean.getBeanName());
根据上面发现加载xml有两种方式
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
关系如下:
两者具有相同血脉的兄弟关系, 一种通过classpath来进行加载,底层是通过ClassLoader 来进行加载资源的。一种是通过文件IO来加载并读取配置xml的。ClassPathXmlApplicationContext
通过classpath 或者相对路径来加载bean 定义文件 资源路径 使用 configLocations 相对路径使用 (path,Class<?>)
FileSystemXmlApplicationContext
改写了 DefaultResourceLoader
的 getResourceByPath(String path)
方法原本默认采用 ClassPathResource
来加载
/**
* Resolve resource paths as file system paths.
* <p>Note: Even if a given path starts with a slash, it will get
* interpreted as relative to the current VM working directory.
* This is consistent with the semantics in a Servlet container.
* @param path path to the resource
* @return the Resource handle
* @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
*/
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
一个通过classpath 来加载,一个通过获取文件的绝对路径 然后通过文件IO来加载,相较 FileSystemXmlApplicationContext
将配置与IOC容器分离开来。
AbstractXmlApplicationContext
说完儿子 来说下爹这货主要干了什么事呢?他是没有解析ooxx.xml
功能的,但是他加载了一个 XmlBeanDefinitionReader
,字面意思就是用来读取声明bean的xml文件的,也就是说这货为了读取xml,自己干不了委托别人来搞,具体薪资不详,我们来看一下AbstractXmlApplicationContext
的核心代码片段
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
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);
}
/**
* Initialize the bean definition reader used for loading the bean
* definitions of this context. Default implementation is empty.
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
* or using a different XmlBeanDefinitionParser implementation.
* @param reader the bean definition reader used by this context
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
reader.setValidating(this.validating);
}
可以看出该方法 就是一个初始化 XmlBeanDefinitionReader
,然后加载bean定义的过程,但是这里的一顿操作还是很精妙的 initBeanDefinitionReader
被提取为一个protected
方法 也就是说 初始化 XmlBeanDefinitionReader
的方法也可以让儿子们来干,根据儿子们的不同口味量身定制。这在实际开发中是十分值得借鉴的。
饭也吃了,酒也喝了,要枪给枪要钱给钱,舒服之后最终该干活了, loadBeanDefinitions
命令一出 XmlBeanDefinitionReader
开始干活
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
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);
}
}
此处有必要简单搞一下 这个干活的
继承了 两个接口
BeanDefinitionReader
通过此接口 bean 被定义 保存到注册表中核心有两类重载方法loadBeanDefinitions(String location)
通过路径来加载int loadBeanDefinitions(Resource resource)
通过资源来加载
EnvironmentCapable
用来获取环境参数 详见Environment
主要是 profile、Property 等一些系统的环境参数