spring 源码分析--IOC容器初始化四

在第二节中我们销毁了老的bean,第三节中有创建了一个新的DefaultListableBeanFactory 类型的工厂,接着又创建了一个 XmlBeanDefinitionReade类型的reader,顾名思义,这个reader就是去读取我们的配置文件,然后解析,完成初始化,在这一节里,我们要完成的是定位配置文件。

 

1.2.2.1.1.3.1.1 loadBeanDefinitions(configLocations): 定义在 XmlBeanDefinitionReader

的抽象父类 -AbstractBeanDefinitionReader 中:

通过循环参数数组:调用函数 loadBeanDefinitions(locations[i]) ,在该函数中又直接调用: loadBeanDefinitions(location, null) ;

原型 为: loadBeanDefinitions(String location, Set actualResources) 定义如下:

====================================================================

// 这里得到当前定义的 ResourceLoader , 默认的我们使用 DefaultResourceLoader

ResourceLoader resourceLoader = getResourceLoader() ;

// 如果没有找到我们需要的 ResourceLoader ,直接抛出异常

if (resourceLoader == null ) {

throw new BeanDefinitionStoreException(

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

}

/**

* 由 getResourceLoader() 函数可知 resourceLoader 为

* PathMatchingResourcePatternResolver 类型,而

* PathMatchingResourcePatternResolver 继承 ResourcePatternResolver

* 这里处理我们在定义位置时使用的各种 pattern, 需要 ResourcePatternResolver 来完成

*/

if (resourceLoader instanceof ResourcePatternResolver) {

try {

Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location) ;

/**

* 自此对资源文件的装载过程结束。下面是对文件的解析和初始化 bean 过程,下面的函数最终

* 会调用 XmlBeanDefinitionReader 类的 loadBeanDefinitions(EncodedResource

* encodedResource)

*/

    int loadCount = loadBeanDefinitions(resources) ;

    if (actualResources != null ) {

        for ( int i = 0; i < resources. length ; i++) {

           actualResources.add(resources[i]) ;

        }

}

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.

    // 这里通过 ResourceLoader 来完成位置定位

    Resource resource = resourceLoader.getResource(location);

    // 这里已经把一个位置定义转化为 Resource 接口,可以供 XmlBeanDefinitionReader 来使用了

    int loadCount = loadBeanDefinitions(resource);

    if (actualResources != null ) {

       actualResources.add(resource) ;

    }

    if ( logger .isDebugEnabled()) {

       logger .debug( "Loaded " + loadCount + " bean definitions from location [" + location + "]" );

    }

    return loadCount;

}

====================================================================

 

 

描述:

1.2.2.1.1.3.1.1.1 getResourceLoader() :从类图中可以看出在 AbstractBeanDefinitionReader 中有 private ResourceLoader resourceLoader ;即是此处的 resourceLoader ;然后注意到 XmlBeanDefinitionReader 的构造函数:

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {

       super (registry);

}

回到父类 AbstractBeanDefinitionReader 的构造函数:

====================================================================

Assert.notNull (registry, "BeanDefinitionRegistry must not be null" );

this . registry = registry;

// Determine ResourceLoader to use.

if ( this . registry instanceof ResourceLoader) {

    this . resourceLoader = (ResourceLoader) this . registry ;

} else {

    this . resourceLoader = new PathMatchingResourcePatternResolver ();

}

====================================================================

可以看出在构造函数中,如果传入的参数是一个 ResourceLoader 类型的对象,那么他将在类型转化后赋给 resourceLoader 。回到 XmlBeanDefinitionReader 创建的地方,即在类: AbstractXmlApplicationContext 的 loadBeanDefinitions 函数里。看到传入的参数是: beanFactory ,找到 beanFactory 的定义处 —AbstractRefreshableApplicationContext 的函数: refreshBeanFactory 里,为:

DefaultListableBeanFactory beanFactory = createBeanFactory() ;由类图中可以看出 beanFactory 不是一个 ResourceLoader 类型,所以执行:

this . resourceLoader = new PathMatchingResourcePatternResolver () ;在构造函数可以看见实际上是创建了一个 DefaultResourceLoader  

 返回。

 

 

1.2.2.1.1.3.1.1.2 getResources(location) :由上面的解释可以知道 resourceLoader 实际上是 PathMatchingResourcePatternResolver 类型,

所以在 PathMatchingResourcePatternResolver 类中的找到 getResources(String locationPattern) 方法如下:

====================================================================

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()))) {

       // 一个 classpath 资源。

        return findPathMatchingResources(locationPattern);

        } else {

       // 多个 classpath 资源

           return findAllClassPathResources(locationPattern.substring( CLASSPATH_ALL_URL_PREFIX .length()));

       }

    } else {

       // Only look for a pattern after a prefix here

       // (to not get fooled by a pattern symbol in a strange prefix).

       int prefixEnd = locationPattern.indexOf( ":" ) + 1;

       if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {

           // 文件类型

           return findPathMatchingResources(locationPattern);

       } else {

           // a single resource with the given name

              return new Resource[] {getResourceLoader().getResource(locationPattern) };

       }

    }

}

====================================================================

我们关注最后一行,即红色部分,这一行的本质是去调用 DefaultResourceLoader    类的

Resource getResource(String location) 方法:方法如下:

====================================================================

public Resource getResource(String location) {

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

    // 如果是类路径的方式,那需要使用 ClassPathResource 来得到 bean 文件的资源对象

    if (location.startsWith( CLASSPATH_URL_PREFIX )) {

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

    } else {

       try {

           // Try to parse the location as a URL...

           // 如果是 URL 方式,使用 UrlResource 作为 bean 文件的资源对象  

           URL url = new URL(location);

           return new UrlResource(url);

       } catch (MalformedURLException ex) {

           // 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了  

           // No URL -> resolve as resource path.

           return getResourceByPath(location);

       }

    }

}

====================================================================

我们的 FileSystemXmlApplicationContext 本身就是是 DefaultResourceLoader 的实现类,他实现了以下的接口:

====================================================================

protected Resource getResourceByPath(String path) {

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

           path = path.substring(1);

       }

       // 这里使用文件系统资源对象来定义 bean 文件  

       return new FileSystemResource (path);

    }

====================================================================

这样代码就回到了 FileSystemXmlApplicationContext 中来,他提供了 FileSystemResource 来完成从文件系统得到配置文件的资源定义。这样,就可以从文件系统路径上对 IOC 配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在 Spring 中我们看到它提供的各种资源抽象,比如 ClassPathResource , URLResource ,FileSystemResource 等来供我们使用。上面我们看到的是定位 Resource 的一个过程,而这只是加载过程的一 部分 - 我们回到 AbstractBeanDefinitionReaderz 中的 loadDefinitions(resource) 来看看得到代表 bean 文 件的资源定义以后的载入过程 , 默认的我们使用 XmlBeanDefinitionReader返回

 

 

 

本站支持 pay for your wishes

相关推荐