spring技术内幕3-IOC容器载入Bean定义资源
来源:互联网 发布:node.js web开发实战 编辑:程序博客网 时间:2024/06/05 13:30
1、Spring IoC容器将Bean定义的资源文件封装为Spring的Resource之后,接下来要做的就是通过Spring的资源加载器(resourceLoader)读入Bean定义资源文件。对于IoC容器来说,Bean定义的加载过程就是将Bean定义资源文件读入内存并解析转换成Spring所管理的Bean的数据结构的过程。
2、以FileSystemXmlApplicationContext为例分析其载入和解析Bean定义资源文件的过程,FileSystemXmlApplicationContext的入口构造函数的源码如下:
public FileSystemXmlApplicationContext(String[] configLocations,boolean refresh,ApplicationContext parent) throws BeansException {
//调用父类构造方法,为容器设置资源加载器(resourceLoader)
super(parent);
//调用父类AbstractRefreshableConfigApplicationContext的方法,设置Bean定义的资源文件,完成IoC容器Bean定义资源的定位
setConfigLocations(configLocations);
if(refresh){
//调用父类AbstractApplicationContext的refresh()方法启动载入Bean定义的过程,是IoC容器载入Bean定义的入口
refresh();
}
}
Spring IoC容器对Bean定义资源的载入是从refresh()方法开始的,FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程。
3、AbstractApplicationContext的refresh函数载入Bean定义过程:
public void refresh() throws BeansException,IllegalStateException {
synchronized(this.startupShutdownMonitor){
//调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识
prepareRefresh();
//告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为BeanFactory配置容器特性,例如类加载器、时间处理器等
prepareBeanFactory(beanFactory);
try{
//为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
//调用所有注册的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
//为BeanFactory注册BeanPost事件处理器
//BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
//初始化信息源,和国际化相关
initMessageSource();
//初始化容器事件传播器
initApplicationEventMulticaster();
//调用子类的某些特殊Bean初始化方法
onRefresh();
//为事件传播器注册事件监听器
registerListeners();
//初始化所有剩余的单态Bean
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch(BeansException ex){
//销毁已创建的单态Bean
destroyBeans();
//取消refresh操作,重置容器的同步标识
cancelRefresh(ex);
throws ex;
}
}
}
refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中"ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();"这句以后的代码都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。
AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean定义资源文件的过程,代码如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory(){
//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if(logger.isDebugEnabled()){
logger.debug("Bean factory for " + getDisplayName() + " : " + beanFactory);
}
return beanFactory;
}
4、AbstractApplicationContext子类的refreshBeanFactory()方法:
AbstractApplicationContext类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,方法的源码如下:
protected final void refreshBeanFactory() throws BeansException {
if(hasBeanFactory()) { //如果已经有容器,销毁容器的bean,关闭容器
destroyBeans();
closeBeanFactory();
}
try{
//创建Ioc容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor){
this.beanFactory = beanFactory;
}
}
catch(IOException ex){
throws new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(),ex);
}
}
refreshBeanFactory()方法的作用是:在创建IoC容器之前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。
5、AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法
AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,其主要源码如下:
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
.......
//实现父类抽象的载入Bean定义方法
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此容器本身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
//为Bean读取器设置SAX xml解析器
beanDefinitionReader.setEntiryResolver(new ResourceEntityResolver(this));
//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
initBeanDefinitionReader(beanDefinitionReader);
//Bean读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
//XmlBean读取器加载Bean定义资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,IOException {
//获取Bean定义资源的定位
Resource[] configResources = getConfigResources();
if(configResources != null) {
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
reader.loadBeanDefinitions(configResources);
}
//如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
String[] configLocations = getConfigLocations();
if(configLocations != null){
//XmlBean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
reader.loadBeanDefinitions(configLocations);
}
}
//这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法,该方法在ClassPathXmlApplicationContext中进行实现,对于我们举例分析源码FileSystemXmlApplicationContext中没有使用该方法
protected Resource[] getConfigResources(){
return null;
} .....
}
XmlBean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions方法读取Bean定义资源。由于我们使用FileSystemXmlApplicationContext作为例子分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。
6、AbstractBeanDefinitionReader读取Bean定义资源:
AbstractBeanDefinitionReader的loadBeanDefinitions方法的源码如下:
//重载方法,调用下面的loadBeanDefinitions(String,Set<Resource>);方法
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location,null);
}
public int loadBeanDefinitions(String location,Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取在IoC容器初始化过程中设置的资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if(resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if(resourceLoader instanceof ResourcePatternResolver){
try{
//将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源,加载多个指定位置的Bean定义资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
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{
//将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源,加载单个指定位置的Bean定义资源文件
Resource resource = resourceLoader.getResource(loation);
//委派调用其子类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;
}
}
//重载方法,调用loadBeanDefinitions(String);
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;
}
从对AbstractBeanDefinitionReader的loadBeanDefinitions方法源码分析可以看出:
首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源
其次,真正执行加载功能的是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法
7、资源加载器获取要读入的资源:
XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要加载的资源,其源码如下:
//获取Resource的具体实现方法
public Resource getResource(String location){
Assert.notNull(location,"Location must not be null");
//这里除了带有classpath标识的Resource
if(location.startsWith(CLASSPATH_URL_PREFIX)){
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()),getClassLoader());
}
else{
try{
//这里处理URL标识的Resource定位
URL url = new URL(location);
return new UrlResource(url);
}
catch(MalformedURLException ex){
//如果既不是classpath标识,又不是URL标识的Resource,则调用容器本身的getResourceByPath方法获取Resource
return getResourceByPath(location);
}
}
}
FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现,就是为了处理既不是classpath标识,又不是URL标识的Resource定位这种情况。
现在,Bean定义的Resource得到了下面我们继续跟踪程序执行方向,分析XmlBeanDefinitionReader的loadBeanDefinitions方法。
8、XmlBeanDefinitionReader加载Bean定义资源:
//XmlBeanDefinitionReader加载资源的入口方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//将读入的XML资源进行特殊编码处理
return loadBeanDefinitions(new EncodedResource(resource));
}
//这里是载入XML形式Bean定义资源文件方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource,"EncodedResource must not be null");
if(logger.isInforEnabled()){
logger.info("Loading XML bean definitions from " +encodedResource.getResource());
}
//这里获取线程局部变量
Set<EncodedResource> currentResource = 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{
//将资源文件转换为IO输入流
InputStream 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();
}
}
}
//从特定XML文件中实际载入Bean定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource,Resource resource) throws BeanDefinitionStoreException{
try{
int validationMode = getValidationModeForResource(resource);
//将XML文件转换为DOM对象,解析过程由documentLoader实现
Document doc = this.documentLoader.loadDocument(inputSource,getEntityResolver(),this.errorHandler,validationMode,isNamespaceAware());
//这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
return registerBeanDefinitions(doc,resource);
}
catch(BeanDefinitionStoreException ex){
throw ex;
}
catch(SAXParsingException ex){
throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid" ,ex);
}
catch(SAXException ex){
throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + "is invalid",ex);
}
catch(ParserConfigurationException ex){
throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource,ex);
}
catch(IOException ex){
throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsng XML document from " + resource,ex);
}
catch(Throwable ex){
throw new BeanDefinitionStoreException(resource.getDescription(),"Unexcepted exception parsing XML document from " + resource,ex);
}
}
通过源码分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现。
9、DocumentLoader将Bean定义资源为Document对象:
DocumentLoader将Bean定义资源转换成Document对象的源码如下:
//使用标准的JAXP将载入的Bean定义资源转换成document对象
public Document loadDocument(InputSource inputSource,EntityResolver entityResolver,ErrorHandler errorHandler,int validationMode,boolean namespaceAware) throws Exception {
//创建文件解析器工厂
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode,namespaceAware);
if(logger.isDebugEnabled()){
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//创建文档解析器
DocumentBuilder builder = createDocumentBuilder(factory,entityResover,errorHandler);
//解析spring的Bean定义资源
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode,boolean namespaceAwarre) throws ParserConfigurationException {
//创建文档解析工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//设置解析XML的校验
if(validationMode != XmlValidationModeDetector.VALIDATION_NODE){
factory.setValidating(true);
if(validationMode == XmlValidationModeDetector.VALIDATION_XSD){
factory.setNamespaceAware(true);
try{
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE,XSD_SCHEMA_LANGUAGE);
}
catch(IllegalArgumentException ex){
parserConfigurationException pcex = new ParserConfigurationException("Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema.Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
该解析过程调用JavaEE标准的JAXP标准进行处理。
至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成Document对象过程完成。
- spring技术内幕3-IOC容器载入Bean定义资源
- 《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件
- 《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件 .
- 《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件
- 《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件
- spring技术内幕4-IoC容器解析Bean定义资源并注册解析后的Bean
- Spring源码学习IOC(3):IoC容器载入Bean定义资源文件
- spring技术内幕2-IOC定位Bean定义资源
- 《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
- 《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
- 《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
- 【Spring Framework 深入】—— IoC容器初始化 -> Bean定义资源的载入解析
- Spring框架学习【IoC容器载入Bean资源文件】
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源 .
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 《Spring技术内幕》学习笔记2——IoC定位Bean定义资源
- 本地wampserver如何配置伪静态
- tp集成支付宝担保支付
- 活动与任务
- 8个开发必备的PHP功能(转)
- CentOS 6.4 图文安装教…
- spring技术内幕3-IOC容器载入Bean定义资源
- mysql中char与varchar的区别分析
- 论文读书笔记-Bengio
- PHP+jQuery+Ajax实现用户登录与退…
- 隐藏/显示 我的电脑盘符驱动…
- jQuery Ajax 实例 全解析
- 关闭浏览器触发事件
- Jquery关闭离开页面时提醒
- android mtk 建一个设备misc