您的位置:首页 > 编程语言 > Java开发

Spring IOC容器的初始化过程--资源定位

2017-01-01 21:31 597 查看
      IOC容器的初始化是又refresh()方法启动的,这个方法标志着IOC容器的正式启动。具体来说,这个启动包括bean的resource定位,载入和注册三个过程。今天我们主要了解资源定位的过程。


定位

       以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。

ClassPathResource res=new ClassPathResource("beans.xml");


       这里定义的Resource并不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。我们也可以看到使用Application­Context 相对于直接使用DefaultListableBeanFactory的好处。因为在ApplicationContext中,spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它配置特定的读取器才能完成这些功能。
        当然, 有利就有弊, 使用DefaultListableBeanFactory这种更底层的容器,能提高定制IoC容器的灵活性。

        FilesystemXmlApplicationContext可以从文件系统载入Resource,ClassPathXmlApplication­Context可以从ClassPath载入Resource,,XmlWebApplicationContext可以在Web容器中载入Resource。

       以FileSystemXmlApplicationContext为例, 通过分析这个ApplicationContext的实现来看看它是怎样完成这个Resource定位过程的。下面是这个ApplicationContext的继承体系。



图1 
 FilesystemXmlApplicationContext的继承体系

从源代码角度看:



                                          图2    代码角度看FilesystemXmlApplicationContext的继承体系


FilesystemXmlApplicationContext.class

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {



public FileSystemXmlApplicationContext() {

}



public FileSystemXmlApplicationContext(ApplicationContext parent) {

super(parent);

}


/**

* Create a new FileSystemXmlApplicationContext, loading the definitions

* from the given XML file and automatically refreshing the context.

* @param configLocation file path

* @throws BeansException if context creation failed

*/

public FileSystemXmlApplicationContext(String configLocation) throws BeansException {

this(new String[] {configLocation}, true, null);

}


/**

* Create a new FileSystemXmlApplicationContext, loading the definitions

* from the given XML files and automatically refreshing the context.

* @param configLocations array of file paths

* @throws BeansException if context creation failed

*/

public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {

this(configLocations, true, null);

}


/**

* Create a new FileSystemXmlApplicationContext with the given parent,

* loading the definitions from the given XML files and automatically

* refreshing the context.

* @param configLocations array of file paths

* @param parent the parent context

* @throws BeansException if context creation failed

*/

public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {

this(configLocations, true, parent);

}


/**

* Create a new FileSystemXmlApplicationContext, loading the definitions

* from the given XML files.

* @param configLocations array of file paths

* @param refresh whether to automatically refresh the context,

* loading all bean definitions and creating all singletons.

* Alternatively, call refresh manually after further configuring the context.

* @throws BeansException if context creation failed

* @see #refresh()

*/

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {

this(configLocations, refresh, null);

}


/**

* Create a new FileSystemXmlApplicationContext with the given parent,

* loading the definitions from the given XML files.

* @param configLocations array of file paths

* @param refresh whether to automatically refresh the context,

* loading all bean definitions and creating all singletons.

* Alternatively, call refresh manually after further configuring the context.

* @param parent the parent context

* @throws BeansException if context creation failed

* @see #refresh()

*/

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)

throws BeansException {


super(parent);

setConfigLocations(configLocations);

if (refresh) {

refresh();

}

}



/**

* 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 Resource handle

* @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath

*/

@Override

protected Resource getResourceByPath(String path) {

if (path != null && path.startsWith("/")) {

path = path.substring(1);

}

return new FileSystemResource(path);

}


}


       我们可以看到在构造函数中,实现了对configuration进行处理的功能。让所有配置在文件系统中的,以.XML 文件方式存在的BeanDefnition都能够得到有效的处理。实现了 getResourceByPath方法,这个方法是个模板方法,是为读取Resource服务的。对于IoC容器功能的实现,这里没有涉及,因为它继承了AbstractXmlApplicationContext
。关于IoC容器功能相关的实现,都是在FileSysternXmlApplicationContext中完成的,但是在构造函数中通过refresh来启动IoC容器的初始化,这个refresh方法非常重要,也是我们以后分析容器初始化过程实现的一个重要入口。

       关于读入器的配置,可以到它的基类AbstractXmlApplicationContext中查看。

      


                                                           图3   getResourceByPath的调用关系



                                                 图4   getResourceByPath的调用过程

我们重点AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现。


AbstractRefreshableApplicationContext.class

protected final void refreshBeanFactory() throws BeansException {

//这里判断,如果已经建立了BeanFactory,则销毁并关闭该工厂。

if (hasBeanFactory()) {

destroyBeans();

closeBeanFactory();

}

//创建并设置持有的DefaultListableBeanFactory的地方同时调用loadBeanDefinitions载入beandefinition信息

try {

DefaultListableBeanFactory beanFactory = createBeanFactory();

beanFactory.setSerializationId(getId());

customizeBeanFactory(beanFactory);

loadBeanDefinitions(beanFactory);

synchronized (this.beanFactoryMonitor) {

this.beanFactory = beanFactory;

}

}

catch (IOException ex) {

throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);

}

}

//在上下文中创建DefaultListableBeanFactory的地方载入bean定义,因为允许有多种载入方式,虽然用得最多的是XML定义的形式,

//这里通过一个抽象函数把具体的实现委托给子类来完成

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

throws BeansException, IOException;


AbstractBeanDefinitionReader.class

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {

//这里取得ResourceLoader,使用的是DefaultResourceLoader

ResourceLoader resourceLoader = getResourceLoader();

if (resourceLoader == null) {

throw new BeanDefinitionStoreException(

"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");

}

//对Resource的路径模式进行解析,得到需要的Resource集合

if (resourceLoader instanceof ResourcePatternResolver) {

// Resource pattern matching available.

	try {

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;

}

}


DefaultResourceLoader.class

//对于取得Resource的具体过程,看DefaultResourceLoader是怎样完成的。

public Resource getResource(String location) {

Assert.notNull(location, "Location must not be null");

//这里处理带有classpath标识的Resource

if (location.startsWith("/")) {

return getResourceByPath(location);

}

else if (location.startsWith(CLASSPATH_URL_PREFIX)) {

return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());

}

else {

	try {

//这里处理带有URL标识的Resource

URL url = new URL(location);

return new UrlResource(url);

}

catch (MalformedURLException ex) {

// No URL -> resolve as resource path.

//两者都不是,用此方法,默认得到一个ClassPathContextResource,这个方法常用子类来实现

//前面看到子类FilesystemXmlApplicationContext实现此方法,返回一个Filesystemresource。

	return getResourceByPath(location);

}

}

}


        
         其他ApplicationContext会对应生成其他种类的Resource。下图中我们可以看到Resource类的继承关系。

 


                                                图5   Resource的定义和继承关系

       
       我们通过对FilesystemXmlApplicationContext的实现原理为例,了解了Resource定位问题的解决方案。定位完成,接下来就是对返回的Resource对象进行载入了。接下来我们会介绍BeanDdfinition的载入和解析过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring IOC