我是靠谱客的博主 壮观白云,这篇文章主要介绍Spring源码学习【二】IOC容器的初始化(一)Resource定位一、总览二、源码分析,现在分享给大家,希望可以做个参考。

目录

一、总览

二、源码分析

1. refresh

2. obtainFreshBeanFactory

3. refreshBeanFactory

4-5. loadBeanDefinitions

6. loadBeanDefinitions

7. getResources

8. getResource


一、总览

在使用IOC容器之前,需要定义一个Resource来定位容器BeanDefinition的资源文件,Resource类继承关系如图1所示,参考使用XmlBeanFactory 和DefaultListableBeanFactory两个IOC容器时,均使用了ClassPathRescource作为BeanDefinition数据源,如下所示:

复制代码
1
ClassPathResource resource = new ClassPathResource("beans.xml");
图1 Resource类继承关系

我们常用的ApplicationContext容器为我们提供了一系列加载不同Resource的功能,比如FileSystemApplicationContext、ClassPathXmlApplicationContext、XmlWebApplicationContext等,下面我们以FileSytemXmlApplicationContext为例,看一看ApplicationContext的Resource定位过程。

FileSystemApplicationContext的类继承关系图请参考Spring源码学习【一】初识IOC容器

其继承自AbstractXmlApplicationContext,IOC容器的功能由其父类实现,其主要是扩展了从文件系统读取BeanDefinition配置文件的功能,体现在覆盖了DefaultResourceLoader的getResourceByPath方法(参考 Spring源码学习【一】初识IOC容器)。

在FileSystemXmlApplicationContext的构造方法中,调用了refresh()方法来启动IOC容器的初始化,这是整个IOC容器初始化的入口,具体的调用的过程如图2所示,下面从源码的角度对这个过程进行分析。

图2 refresh() 调用时序图

二、源码分析

1. refresh

refresh()方法在FileSystemXmlApplicationContext的构造方法中调用,这一方法定义在AbstractApplicationContext中,启动了IOC容器的初始化过程,如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext { /** * 创建一个应用上下文,根据应用环境解析路径,并启动refresh()过程 */ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } }

2. obtainFreshBeanFactory

obtainFreshBeanFactory用于通知子类刷新内部的bean factory,其中调用了refreshBeanFactory方法,这一方法在AbstractApplicationContext中未给出具体实现,留给其子类实现,最终调用的为其子类AbstractRefreshableApplicationContext的refreshBeanFactory方法,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements Configu-rableApplicationContext { …… public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { …… // 通知子类刷新内部的bean factory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); …… } } protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException; }

3. refreshBeanFactory

refreshBeanFactory由AbstractRefreshableApplicationContext类实现,且声明为final方法,不可以被覆盖。在这个方法中,首先判断如果已经创建了beanFactory,则销毁bean并关闭beanFactory,然后创建一个新的DefaultListableBeanFactory作为应用上下文的IOC容器并由当前类对象持有这个beanFactory,同时调用loadBeanDefinitions方法载入BeanDefinition。这里的loadBeanDefinitions是一个抽象方法,留给其子类实现。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext{ …… @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { // 若已创建则销毁bean关闭beanFactory destroyBeans(); closeBeanFactory(); } // 创建一个新的DefaultListableBeanFactory作为应用上下文的IOC容器 try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); // 载入BeanDefinition loadBeanDefinitions(beanFactory); // 由当前类对象持有这个beanFactory synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException; }

4-5. loadBeanDefinitions

loadBeanDefinitions由AbstractXmlApplicationContext实现,在这个方法中,首先创建了一个XmlBeanDefinitionReader对象,并将这个reader回调给由父类创建的DefaultListableBeanFactory对象,然后对reader进行了一系列配置,最后调用了reader的loadBeanDefinitions方法,代码如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplication-Context { …… @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 为给定的beanFactory创建一个XmlBeanDefnitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 用当前上下文的资源加载环境配置reader beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 初始化reader initBeanDefinitionReader(beanDefinitionReader); // 启动beanDefinition的加载 loadBeanDefinitions(beanDefinitionReader); } 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); } } }

注:

复制代码
1
beanDefinitionReader.setResourceLoader(this);

将这个ApplicationContext对象作为了reader的ResourceLoader,根据之前的分析我们能够知道,AbstractXmlApplicationContext间接继承了DefaultResourceLoader,而DefaultResourceLoader又实现了ResourceLoader接口,所以整个继承关系中的ApplicationContext类均为ResourceLoader类型的实例,在最后调用resourceLoader的getResource方法时实则调用了ApplicationContext的getResourceByPath方法,这就是为什么FileSystemXmlApplicationContext重写了getResourceByPath方法就可以实现从文件系统读取XML格式的配置文件。

6. loadBeanDefinitions

这一步调用了XmlBeanDefinitionReader父类AbstractBeanDefinitionReader的loadBeanDefinitions方法,这里有一系列loadBeanDefinitions方法的重载,在最终的调用方法中我们可以看到,对resourceLoader的类型进行了判断,如果resourceLoader是ResourcePatternResolver类型的实例则以通配符模式定义的路径定位资源,否则直接通过路径定位资源。

这里的resourceLoader正是上一步为reader设置的ApplicationContext实例,如图3所示

图3 AbstractApplicationContext 类图

AbstractAppliactionContext实现了ResourcePatternResolver接口,作为ResourcePatternResolver类型的resourceLoader实例对象使用,并调用了getResources方法来获得Resource资源对象,代码如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader { …… @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException{ Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { counter += loadBeanDefinitions(resource); } return counter; } @Override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // 通配符模式匹配方式 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 { // 通过绝对路径获取单个资源 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; } } @Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; for (String location : locations) { counter += loadBeanDefinitions(location); } return counter; } }

这里调用的getResources方法是定义在ResourcePatternResolver接口中的方法,具体由PathMatchingResourcePatternResolver类实现,在AbstractApplicationContext中持有PathMatchingResourcePatternResolver实例,因此这一过程中调用的getResources实际为PathMatchingResourcePatternResolver对象的getResources方法。

7. getResources

getResources方法由PathMatchingResourcePatternResolver实现,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { …… @Override public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // 类路径资源,可能有多个资源文件 if(getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // 获取所有可匹配该包含’?’或’*’的类路径模式的资源 return findPathMatchingResources(locationPattern); } else { // 获取所有给定的类路径资源 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :locationPattern.indexOf(':') + 1); if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // 获取所有可匹配该包含’?’或’*’的类路径模式的资源 return findPathMatchingResources(locationPattern); } else { // 获得给定路径的单个资源 return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } } }

8. getResource

最终调用了resourceLoader的getResource方法,并在其中调用了getResourceByPath方法。如下为DefaultResourceLoader的实现,但在FileSystemXmlApplicationContext的实现中,实际上调用了被覆盖的getResourceByPath方法,从而实现了从文件系统读取资源文件的功能。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DefaultResourceLoader implements ResourceLoader { …… @Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); for (ProtocolResolver protocolResolver : this.protocolResolvers) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return 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路径 URL url = new URL(location); return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlRe-source(url)); } catch (MalformedURLException ex) { // 非URL路径,以普通路径形式解析 return getResourceByPath(location); } } } protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); } }

至此,FileSystemXmlApplicationContext就完成了FileSystemResource的定位工作,有了这个Resource,下一步就可以进行BeanDefinition的载入和注册过程了。

最后

以上就是壮观白云最近收集整理的关于Spring源码学习【二】IOC容器的初始化(一)Resource定位一、总览二、源码分析的全部内容,更多相关Spring源码学习【二】IOC容器内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(101)

评论列表共有 0 条评论

立即
投稿
返回
顶部