spring源码之解析配置文件原理
上篇博文提到,spring利用监听器实现在tomcat启动的时候实现初始化。在初始化的过程中,实例化一个webapplicationContext对象,然后利用这个对象的refresh()方法解析配置文件并完成初始化.这篇博文就详细来探讨下这个refresh()方法.
//代码清单:refresh()
publicvoid refresh() throws BeansException,IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1.容器启动的预先准备,记录容器启动的时间和标记.
prepareRefresh();
//2.创建BeanFactory,如果已有就销毁,没有就创建.此类实现了对BeanDefinition的装载
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3.配置BeanFactory标准上下文特性,如类装载器、postProcesser等
prepareBeanFactory(beanFactory);
try {
// 4.在bean被装载后,提供一个修改BeanFactory的入口
postProcessBeanFactory(beanFactory);
// 5.调用postProceessBeanFactory
invokeBeanFactoryPostProcessors(beanFactory);
//6.注册用于拦截bean创建过程中的BeanPostProcessors
registerBeanPostProcessors(beanFactory);
// 7.Initialize message source for this context.
initMessageSource();
// 8.Initialize event multicaster for this context.
initApplicationEventMulticaster();
//9.Initialize other special beans in specific contextsubclasses.
onRefresh();
//10. 注册监听器
registerListeners();
//11.完成容器的初始化,里面的preInstantiateSingletons完成单例对象的创建
finishBeanFactoryInitialization(beanFactory);
// 12.Last step: publish corresponding event.
}
}
一.prepareRefresh
protected void prepareRefresh() {
//记录容器启动的时间.
this.startupDate = System.currentTimeMillis();
//记录标记.
synchronized (this.activeMonitor) {
this.active = true; }
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
}
这个方法主要是记录容器启动的时间和一些日志信息,对系统并没什么影响。
二.obtainFreshBeanFactory
这个方法就是本篇博文讨论的重点-解析配置文件applicationContext.xml
1.解析配置文件步骤
1.1.ioc容器创建步骤
①创建一个beanFactory(application)
②beanFactory中添加一个读取配置信息的reader(DefaultBeanDefinitionDocumentReader)
③创建ioc配置文件抽象资源(Resouce)
④读取配置信息reader.load()
这个步骤不难理解。spring的核心是ioc容器,把配置文件中的对象放入容器中,那我们首先得有一个容器吧?所以这个步骤通俗地理解为:先创建一个盛放对象的容器(BeanFactory),然后创建一个解析器(reader),再获取配置文件信息(resouce),最后用解析器解析配置文件,把解析出来的对象放到这个容器中。
1.2.简单的代码实现
//1.先创建一个IOC容器
DefaultListabeBeanFactory factory=new DefaultListableBeanFactory();
//2.获取一个解析器
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
//3.获取资源文件
ClassPathResouce res=new ClassPathResource("beans.xml");
//4.解析配置文件
reader.loadBeanDefinitions(res);
正如struct2一样,解析配置文件,会把配置文件中的信息封装到不同的对象中。spring把不同的配置信息封装到beanDefinitions接口的不同实现类中。因为配置文件可能会配置在不同的地方,比如在web.xml,或者是要在file system中,又或者是在classpath目录下,针对这种情况,spring设计了一个resouce接口,来统一了这些路径。只要你实现了相应的resouce类,就能加载相应路径下的配置文件。具体请参考后面有关spring的策略模式的讲解。Spring的BeanFacotry是一个类工厂,使用它来创建各种类型的Bean,最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean。
下面我们就来看看spring是如何实现上述的这些步骤的。
2.解析配置文件具体实现
【代码清单】:obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//创建一个IOC容器beanFactory,并解析配置文件
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
return beanFactory;
}
2.1.创建一个IOC容器beanFactory
//【代码清单】:refreshBeanFactory()
protected final void refreshBeanFactory()throws BeansException {
//判断beanFactory是否已存在
if (hasBeanFactory()){
//存在就销毁beans
destroyBeans();
//关闭工厂
closeBeanFactory();
}
try {
//不存在,就创建beanFactory, DefaultListableBeanFactory是beanFactory的实现类
DefaultListableBeanFactorybeanFactory = createBeanFactory();
customizeBeanFactory(beanFactory);
//创建一个reader,默认是XmlBeanDefinitionReader,并解析,把对每个标签的解析结果都封装到相应的beanDefinition对象,然后把这个对象注册到DefaultListableBeanFactory
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
}
创建beanFactory,其实就是new 一个实现类。
//【代码清单】:createBeanFactory()
protected DefaultListableBeanFactory createBeanFactory() {
//DefaultListableBeanFactory是beanFactory的实现类
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
2.2.实例化一个解析器
//【代码清单】:loadBeanDefinitions(beanFactory)
protectedvoid loadBeanDefinitions(DefaultListableBeanFactorybeanFactory) throws IOException {
// 实例化一个reader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//配置loader环境,也就是ioc配置文件抽象资源,ResourceLoder定位bean资源
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));
// 空方法
initBeanDefinitionReader(beanDefinitionReader);
//载入bean信息,并对其进行处理
loadBeanDefinitions(beanDefinitionReader);
}
这里创建了一个解析器xmlBeanDefinitonReader。看它的名字,就知道它的作用了。BeanDefinition是封装配置信息的对象,reader是读取,xml是配置文件,因此这个对象就是applictionContext.xml的解析器。下面的工作就是获取配置文件了。
2.3.获取配置文件resouce
//【代码清单】:loadBeanDefinitions(beanDefinitionReader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取web.xml中的contextConfigLocation的配置信息
String[] configLocations = getConfigLocations();
//循环遍历解析
if (configLocations != null) {
for (int i = 0; i < configLocations.length; i++) {
reader.loadBeanDefinitions(configLocations[i]);
}
}
}
这个configLoactions是在监听器listener中就已经设置好的了
wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
这里用get()方法,获取这一配置信息。因为,在web.xml中可以配置多个applicationContext.xml,所以这里循环遍历来解析。
<!-- 指定spring的配置文件,默认从web根目录寻找配置文件,classpath从类路径寻找,tomacat启动的时候,实例化spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param>
上面说到,spring设计了一个resouce接口来管理配置文件。所以下面还得根据这个路径,来获得一个resouce对象。
//【代码清单】:reader.loadBeanDefinitions(String)
publicint loadBeanDefinitions(Stringlocation, Set actualResources) throws BeanDefinitionStoreException {
//得到当前的ResourceLoader,默认使用DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
//如果没有ResourceLoader则抛出异常,此处代码省略
if (resourceLoader instanceof ResourcePatternResolver) {
// 这里处理我们在定义位置时使用的各种pattern,需要ResourcePatternResolver.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//载入资源
int loadCount =loadBeanDefinitions(resources);
if (actualResources!= null) {
for (int i = 0; i <resources.length; i++) {
actualResources.add(resources[i]);
}
}
return loadCount;
}
}
else {
// 这里通过ResourceLoader来完成位置定位,一个位置定义转化为Resouce接口后可以供XmlBeanDefinitionReader来使用了
Resource resource = resourceLoader.getResource(location);
//这里就是解析配置开始了!!!
int loadCount =loadBeanDefinitions(resource);
if (actualResources!= null) {
actualResources.add(resource);
}
return loadCount;
}
}
这里是策略模式中,强制使用了ResouceLoader的实现类。根据web.xml中配置的字符串,来实现不同的resouce:如果是classpath开头,则实现classpathResouce,如果不是话,就尝试UrlResouce,再不然就是filesystemResouce
//【代码清单】:DefaultResourceLoader.getResource(location)
public Resource getResource(String location) {
//如果路径以classPath:开头,即类路径
if(location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是URL方式,使用UrlResource作为bean资源对象
URLurl = new URL(location);
return new UrlResource(url);
}
catch(MalformedURLException ex) {
//如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了。如FiltSystemXmlApplication提供FileSystemResouce来完成从文件系统得到配置文件资源定义
return getResourceByPath(location);
}
}
}
2.4.解析抽象资源
//【代码清单】:loadBeanDefinitions(resource)
public int loadBeanDefinitions(EncodedResourceencodedResource) throws BeanDefinitionStoreException {
try {
//这里通过Resource得到InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从InputStream中得到XML的解析源
InputSourceinput Source = new InputSource(inputStream);
if(encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//具体的解析和注册过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//关闭从Resouce中得到的IO流
inputStream.close();
}
}
}
当得到一个resouce对象之后,通过重载loadBeanDefinitions(Resouce resouce)开始解析配置文件.通过resouce对象,获得配置文件袋io流,然后通过io流获得xml的解析源.获得xml的解析源之后,就能获得xml的document对象了
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//通过xml源获得document对象
Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
//异常信息
}
获得了document对象,接下来就是我们熟悉的dom解析了,下回合再详解。
三、总结
这篇博文主要介绍了spring解析配置文件的主要流程:先创建一个容器(beanFactory),然后创建一个解析器(reader),获取配置文件(resouce)后,通过这个解析器解析这个配置文件(document)。
转载于:https://blog.51cto.com/yoyanda/1717854