欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-02 16:27:19
...

在第二节中我们销毁了老的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

相关标签: IOC Spring Bean