【Spring Framework 深入】—— IoC容器初始化 -> Bean定义资源的Resource定位
来源:互联网 发布:自动电话营销软件 编辑:程序博客网 时间:2024/06/07 01:07
基本概念
ApplicationContext 继承体系
本文主要关注ApplicationContext的继承体系,至于BeanFactory的分支,以后再研究。
BeanFactory or ApplicationContext?
BeanFactory和ApplicationContext都是实现IoC容器的基础接口。Application是BeanFactory的子接口,包含了BeanFactory的功能,同时增加了对Transactions和AOP的支持。所以官方更推荐开发者使用ApplicationContext及其子类实现IoC容器。特别地,Spring在实现时,大量使用ApplicationContext实现BeanPostProcessor extension point。
官方文档有如下阐述:
The BeanFactory provides the underlying basis for Spring’s IoC functionality but it is only used directly in integration with other third-party frameworks and is now largely historical in nature for most users of Spring.
只有在与第三方框架集成时,才推荐使用BeanFactory。
org.springframework.context.Interface ApplicationContext
All Superinterfaces:(继承接口)
ApplicationEventPublisher, BeanFactory, EnvironmentCapable, HierarchicalBeanFactory, ListableBeanFactory, MessageSource, ResourceLoader, ResourcePatternResolver
ApplicationContext实现了上述接口,丰富了基本IoC容器(BeanFactory)的行为,可以说是一个高级的IoC容器。
从ApplicationContext接口的实现,我们看出其特点:
1. 支持信息源,可以实现国际化。(实现MessageSource接口)
2. 访问资源。(实现ResourcePatternResolver接口)
3. 支持应用事件。(实现ApplicationEventPublisher接口)
它的常用具体类有ClasspathXmlApplicationContext和FileSystemXmlApplicationContext,Web 项目方面有XmlWebApplicationContext。
IoC容器的初始化
IoC容器的初始化主要包括BeanDefinition的Resource定位、载入解析和注册这三个基本的过程。我们以ApplicationContext为例讲解。由于篇幅过大,这篇文章先详细讲解Resource定位。
创建:
ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
构造函数:分3步,分别是调用父类构造函数、setConfigLocations和refresh
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
1.super(parent)的作用是为容器设置Bean资源加载器,通过debug,可知实际是由其父类AbstractApplicationContext 完成设置。注意,从这里可以看到AbstractApplicationContext 继承了DefaultResourceLoader,所以实际上它自身也作为资源加载器。
AbstractApplicationContext.javapublic abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean public AbstractApplicationContext(ApplicationContext parent) { this(); setParent(parent);} public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver();}protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this);}
2.接下来,setConfigLocations(configLocations)的作用是设置Bean定义资源文件的路径,实际是由其父类AbstractRefreshableConfigApplicationContext完成设置
AbstractRefreshableConfigApplicationContext.javapublic abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext implements BeanNameAware, InitializingBean//从这里可以看到配置Bean定义资源文件可以使用两种方式,字符串和字符串数组//字符串会以,; /t/n这些分隔符分割//location="a.xml,b.xml,..."public void setConfigLocation(String location) { //String CONFIG_LOCATION_DELIMITERS = ",; /t/n"; //即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式 setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));}//location=new String[]{“a.xml”,”b.xml”,……} public void setConfigLocations(String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { // resolvePath为同一个类中将字符串解析为路径的方法 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; }}
3.接下来是开始进行Bean定义资源文件加载,由AbstractApplicationContext的refresh函数完成。
refresh函数是一个模板方法,执行多个方法,而且提供了各(protected)方法的(默认)实现,其子类可以重写它们
模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类(使用protected方法)可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
refresh函数中调用了多个方法,这里先不详细讲解每一个方法,可以先通过英文注释大概了解各方法的作用。
AbstractApplicationContext.javapublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. 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(); } } }
refresh()核心调用方法1:obtainFreshBeanFactory函数调用,完成了容器初始化的最重要最基础的功能,Bean定义资源的Resource定位、载入解析和注册。
AbstractApplicationContext.javaConfigurableListableBeanFactory 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;public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
这里使用了委派设计模式,obtainFreshBeanFactory中调用了两个抽象方法,定义了obtainFreshBeanFactory的算法骨架,实际的行为交给其子类(AbstractRefreshableApplicationContext)实现
AbstractRefreshableApplicationContext.java @Override protected final void refreshBeanFactory() throws BeansException { //如果已经有容器,销毁容器中的bean,关闭容器,以保证在refresh之后使用的是新建立起来的IoC容器 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建IoC容器 DefaultListableBeanFactory beanFactory = createBeanFactory(); //对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等 beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //调用载入Bean定义的方法,这里又使用了委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
在这个方法中,先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean
使用了委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器(AbstractXmlApplicationContext)
AbstractXmlApplicationContext.java@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); //为Bean读取器设置资源加载器, //AbstractXmlApplicationContext的祖先父类AbstractApplicationContext继承DefaultResourceLoader, //因此,容器本身也是一个资源加载器 //所以,这个资源加载器由始至终都是容器自身 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
调用了另一个重载函数loadBeanDefinitions(beanDefinitionReader),委托给了XmlBeanDefinitionReader
AbstractXmlApplicationContext.javaprotected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //获取Bean定义资源的定位 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //如果configResources为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源 String[] configLocations = getConfigLocations(); if (configLocations != null) { //XmlBeanDefinitionReader调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源 reader.loadBeanDefinitions(configLocations); } }
这里也使用了委托模式,调用子类的获取Bean定义资源定位的方法(getConfigResources()),该方法在ClassPathXmlApplicationContext中实现,FileSystemXmlApplicationContext默认返回null。
由于FileSystemXmlApplicationContext的getConfigResources返回null,因此程序执行configLocations分支,调用XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的loadBeanDefinitions(String… locations)方法
AbstractBeanDefinitionReader.java@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; }
对每一个location调用loadBeanDefinitions,其抽象父类AbstractBeanDefinitionReader定义了方法骨架
AbstractBeanDefinitionReader.javapublic int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { //1.获取在IoC容器初始化过程中设置的资源加载器,调用 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //2.将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源(Resource) Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //3.委派调用其子类XmlBeanDefinitionReader的方法,加载Resource 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. //和上面步骤2一样,获得Resource。实际调用的是DefaultResourceLoader中的getSource()方法定位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; } }
FileSystemXmlApplicationContext本身就是DefaultResourceLoader的实现类
意思是,AbstractBeanDefinitionReader中ResourceLoader resourceLoader = getResourceLoader();
得到的是FileSystemXmlApplicationContext(AbstractApplicationContext)。
还记得在AbstractXmlApplicationContext中beanDefinitionReader.setResourceLoader(this);
为Bean读取器设置的资源加载器,正是AbstractApplicationContext,因为继承了DefaultResourceLoader,因此容器本身也是一个资源加载器
将指定位置的Bean定义资源文件解析为IoC容器封装的资源(Resource)的语句
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);orResource resource = resourceLoader.getResource(location);
资源加载器获取要读入的资源(Resource)
实际上,调用了DefaultResourceLoader的getResource方法获取Resource。
DefaultResourceLoader@Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith("/")) { return getResourceByPath(location); } //如果是类路径的方式,那需要使用ClassPathResource 来得到bean 文件的资源对象 else 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 url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. return getResourceByPath(location); } } }
至此,完成了Bean定义资源的Resource定位
总结一下从创建容器之后各个父类方法调用,不然就有点懵逼了!
接下来,开始Bean定义资源(已封装成Resource)的载入解析
回到XmlBeanDefinitionReader的loadBeanDefinitions方法
XmlBeanDefinitionReader.java@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //将读入的XML资源进行特殊编码处理 return loadBeanDefinitions(new EncodedResource(resource)); }
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
未完。。。待续
Reference:
Spring Framework Reference Documentation
http://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/htmlsingle/
博客园 —— 牛奶、不加糖
http://www.cnblogs.com/ITtangtang/p/3978349.html#a4
- 【Spring Framework 深入】—— IoC容器初始化 -> Bean定义资源的Resource定位
- 【Spring Framework 深入】—— IoC容器初始化 -> Bean定义资源的载入解析
- 深入解读Spring Framework IoC容器(第八弹:Bean的初始化和销毁)
- 【Spring Framework 深入】—— IoC容器初始化 -> BeanDefinition的注册
- Spring IOC容器的初始化过程--资源定位
- 《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
- 《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
- 《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
- Ioc容器初始化-bean资源定位(2)
- Ioc容器初始化-bean资源定位(3)
- Spring源码阅读之IoC容器初始化1 -- Resource定位
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源 .
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件
- 《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件 .
- jsp页面 utf-8 向后台传值乱码(spring)
- unity资源包的依赖关系(1)
- C语言知识点总结
- JAVA基础之interface
- SoftEhterVPN-master修改win32路由部分
- 【Spring Framework 深入】—— IoC容器初始化 -> Bean定义资源的Resource定位
- Servlet概述与使用
- Linux系统开机过程详解
- <s:iterator>及<s:property>的用法
- 使用aidl工具快速在应用层实现binder进程间通信
- JDK并发工具包CompletionService和ExecutorCompletionService的好处和使用场景
- 再谈深度学习文本的表示
- Axure RP Pro 7.0结账界面标签切换效果
- 配置vsftpd配合filezilla使用