spring技术内幕笔记:IoC容器初始化过程(1)- Resource定位
来源:互联网 发布:上海聚宝网络 编辑:程序博客网 时间:2024/06/05 20:36
Spirng版本:4.3.8.RELEASE
IoC容器初始化过程:
(1)Resource定位过程
Resource定位指的是BeanDefinition的资源定位,由ResourceLoader通过统一的Reource接口完成,这个Reource对各种形式的Beandifinition的使用都提供了统一接口。这个过程类似于容器寻找数据的过程。
(2)BeanDefinition的载入
这个载入过程是把用户定义好的Bean表示成IoC容器的内部数据结构,这个容器内部数据结构就是BeanDefinition,也就是POJO对象在IoC容器中的抽象,通过BeanDefinition IoC容器可以方便的对Bean进行管理
(3)向IoC容器注册这些BeanDefinition
这个过程通过调用BeanDefinitionRegistry接口的实现来完成,这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。在IoC容器内部将BeanDefinition注入到一个HashMap中,通过这个HashMap来持有这些BeanDefinition数据。
Resource资源定位
1.DefaultListableBeanFactory
使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition,这时使用的是ClassPathResource,表名spring会在类路径中寻找以文件形式存在的BeanDefinition信息。由于DefaultListableBeanFactory是一个纯粹的IoC容器,还需要为它配置特定的读取器才能完成这些功能。
/*-------IoC容器的建立过程------*/ //加载xml配置文件,实例化资源对象 ClassPathResource resource=new ClassPathResource("beans.xml"); //创建一个BeanFactory,这里使用DefaultListableBeanFactory DefaultListableBeanFactory factory=new DefaultListableBeanFactory(); //创建reader对象 XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory); //载入xml文件形式的Beandefinition,通过一个回调配置给BeanFactory reader.loadBeanDefinitions(resource);
2.ApplicationContext
可以使用Application的实现类FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext来完成Resource的读入功能。
FileSystemXmlApplicationContext:从文件系统载入Resource
ClassPathXmlApplicationContext:从class path载入Resource
XmlWebApplicationContext:在web容器中载入Resource
ApplicationContext的用法:
//使用FileSystemXmlApplicationContext加载配置文件 ApplicationContext context=new FileSystemXmlApplicationContext("classpath*:/spring/applicationContext.xml"); //ClassPathXmlApplicationContext加载 //ApplicationContext context=new ClassPathXmlApplicationContext("classpath*:/spring/applicationContext.xml"); //获取bean Performer duke= (Performer) context.getBean("dukeSinger");//根据id从配置文件中获取bean duke.perform();以FileSystemXmlApplicationContext为例,分析ApplicationContext的实现:
从上图中可以看出FileSystemXmlApplicationContext通过层层关系继承了AbstractApplicationContext,而AbstractApplicationContext又继承了DefaultResourceLoader,因此FileSystemXmlApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力。
(1)FileSystemXmlApplicationContext:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext { public FileSystemXmlApplicationContext() { } public FileSystemXmlApplicationContext(ApplicationContext parent) { super(parent); } /** * 构造函数:需要传入XML资源文件的路径 */ public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } /** * 构造函数:传入多个资源文件 */ public FileSystemXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations, true, null); } /** * 构造函数 * @param configLocations 资源文件数组 * @param parent 双亲IoC容器 */ public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException { this(configLocations, true, parent); } /** * 构造函数 * @param configLocations 资源文件路径 * @param refresh 是否调用refresh方法载入BeanDefinition */ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException { this(configLocations, refresh, null); } /** * 构造函数 * @param configLocations 资源文件路径 * @param refresh 是否调用refresh方法载入BeanDefinition * @param parent 双亲IoC容器 * @throws BeansException */ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh();//分析容器初始化的一个重要入口 } } /** * 通过资源文件路径获取Resource对象,返回的是一个FileSystemResource对象,通过这个对象可以进行相关I/O操作,完成BeanDefinition定位 * @param path 资源文件路径 * @return Resource spring中的资源对象 */ @Override protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }}
1)从第二个构造函数开始,实际上都是调用的FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)这个构造函数
2)通过refresh()方法,启动了对BeanDefinition资源定位的过程,是分析容器初始化的重要入口。
点进refresh()方法,进入AbstractApplicationContext类:
(2)AbstractApplicationContext:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // 调用obtainFreshBeanFactory()方法 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory();//调用了refreshBeanFactory()方法 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } //由子类AbstractRefreshableApplicationContext实现 protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;refresh()方法中,调用了obtainFreshBeanFactory()方法,obtainFreshBeanFactory()方法又调用了refreshBeanFactory方法,在AbstractApplicationContext中,该方法是一个抽象方法,具体的实现由AbstractApplicationContext的子类AbstractRefreshableApplicationContext来实现。
(3)AbstractRefreshableApplicationContext:
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { /** * AbstractRefreshableApplicationContext中实现的refreshBeanFactory()方法 * * @throws BeansException */ @Override protected final void refreshBeanFactory() throws BeansException { //如果已经建立了BeanFactory,销毁并关闭BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //构造了一个BeanFactory,这里使用DefaultListableBeanFactory实现 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //载入BeanDefinition 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的地方 * getInternalParentBeanFactory()的具体实现可以参看AbstractApplicationContext的实现 * * @return */ protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); } /** * 抽象方法,载入BeanDefinition,由子类AbstractXmlApplicationContext实现 * * @param beanFactory * @throws BeansException * @throws IOException */ protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;}
loadBeanDefinitions()方法由由子类AbstractXmlApplicationContext实现
(4)AbstractXmlApplicationContext:
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext { @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //创建XmlBeanDefinitionReader对象,通过回调设置到BeanFactory中,beanFactory使用的也是DefaultListableBeanFactory XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); //ResourceLoader.因为DefaultResourceLoader是父类,所以this可以直接被调用 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //启动Bean定义信息载入的过程,委托给BeanDefinitionReader完成 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { reader.setValidating(this.validating); } /** * 载入BeanDefinition * @param reader * @throws BeansException * @throws IOException */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //以Reource的方式获取配置文件的资源位置 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources);//调用XmlBeanDefinitionReader中的loadBeanDefinitions方法 } //以string方式获取配置文件的位置 String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } } protected Resource[] getConfigResources() { return null; } }XmlBeanDefinitionReader是AbstractBeanDefinitionReader的子类,在AbstractBeanDefinitionReader中查看loadBeanDefinitions方法:
(5)AbstractBeanDefinitionReader:
在loadBeanDefinitions()方法中定义了ResourceLoader对象,由ResourceLoader完成资源的加载。
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, 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集合,这些集合指向我们已经定义好的BeanDefinition信息 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //调用DefaultResourceLoader的getResource完成Resource定位 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 { //调用DefaultResourceLoader的getResource完成Resource定位 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; } }
ResourceLoader其实是使用了DefaultResourceLoader对象的getResource方法完成Resource对象的定位:
(6)DefaultResourceLoader
public class DefaultResourceLoader implements ResourceLoader { public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); Iterator var2 = this.protocolResolvers.iterator(); Resource resource; do { if (!var2.hasNext()) { //处理带有/反斜杠的resource if (location.startsWith("/")) { return this.getResourceByPath(location); } //处理带有calsspath标识的resource if (location.startsWith("classpath:")) { return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader()); } try { //处理URL标识的resource URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException var5) { //如果以上标识都算不上,则把getResource的任务交给getResourceByPath,这个方法是一个protected方法 //默认的实现是得到一个ClassPathContextResource对象,这个方法常常会用子类来实现 return this.getResourceByPath(location); } } ProtocolResolver protocolResolver = (ProtocolResolver) var2.next(); resource = protocolResolver.resolve(location, this); } while (resource == null); return resource; } }FileSystemXmlApplicationContext中有getResourceByPath方法的实现,这个方法返回了一个FileSystemResource对象,通过这个对象,spring可以进行相关的I/O操作,完成BeanDefinition的定位。
再总结一下过程:
1)FileSystemXmlApplicationContext的构造函数中调用了refresh()方法
2)refresh()方法由AbstractApplicationContext实现,在refresh()方法中,调用了obtainFreshBeanFactory()方法,obtainFreshBeanFactory()中又调用了refreshBeanFactory方法
3)refreshBeanFactory()方法由AbstractRefreshableApplicationContext实现,并创建了DefaultListableBeanFactory容器,然后调用了载入BeanDefinition的方法loadBeanDefinitions()
4)loadBeanDefinitions()方法由AbstractXmlApplicationContext实现,在loadBeanDefinitions()方法中又调用了AbstractBeanDefinitionReader中的loadBeanDefinitions方法
5)loadBeanDefinitions()方法中定义了ResourceLoader对象,实际使用的DefaultResourceLoader的getResource方法
6)在DefaultResourceLoader的getResource方法中,根据标识返回对应的Resource对象,完成BeanDefinition的定位。
来源:
(1)spring技术内幕
(2) 竹叶青1986:
Spring源码阅读之IoC容器初始化1 -- Resource定位
- spring技术内幕笔记:IoC容器初始化过程(1)- Resource定位
- spring技术内幕笔记:IoC容器初始化过程(2)- BeanDefinition的载入
- spring技术内幕笔记:IoC容器的初始化过程(3)- BeanDefinition的注册
- Spring源码阅读之IoC容器初始化1 -- Resource定位
- Spring技术内幕之IOC容器的实现(01)-IOC容器初始化过程
- Spring技术内幕之IOC容器的实现(02)-BeanDefinition的Resource定位
- spring 技术内幕--IOC初始化过程深入之BeanDefinition的定位
- 读《Spring技术内幕》-总结3-IoC容器的初始化过程
- spring技术内幕1-IOC容器体系结构
- Spring技术内幕(一):IOC容器
- 【初探Spring】——Spring IOC(三):初始化过程—Resource定位
- Spring IOC容器的初始化过程--资源定位
- Spring技术内幕笔记 -- IoC容器的实现
- 《Spring技术内幕》笔记-第二章 IoC容器的实现
- spring技术内幕笔记:IoC容器的依赖注入
- spring 技术内幕--IOC初始化过程深入之BeanDefinition的载入和解析1
- Spring IOC容器的初始化过程(1)
- spring 技术内幕--IOC初始化之BeanDefinition的在IOC容器中的注册
- Maven的下载安装
- POJ 3252 Round Numbers 数位DP
- HDU 1494 跑跑卡丁车
- 学习日志2017-7-8
- emacs verilog-mode对IC顶层集成的帮助
- spring技术内幕笔记:IoC容器初始化过程(1)- Resource定位
- 网络基础 — CRC校验的认识以及校验原理
- redis发布订阅模式-redis系列教程
- ps学习笔记(一)
- STL之仿函数
- POJ 3601 Subsequence
- 【Linux服务器配置】服务器的配置
- 实践三
- opencv特征检测与匹配