`
游伯度
  • 浏览: 22022 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring IOC - 资源装载

阅读更多

很多时候,真心欣赏 Spring 的封装、分层的方案实现。Spring 对资源文件的统一定义以及获取都是很好封装体现。

 

1. 资源装载的概述

 

在前面的分享中,使用 FileSystemXmlApplicationContext 举例。同时提到了在构建 FileSystemXmlApplicationContext 的时候会调用org.springframework.context.support.AbstractApplicationContext#refresh方法。在这个 refresh 方法中会调用AbstractApplicationContext中的refreshBeanFactory方法,这个方法就是加载资源去触发点。下图是是具体调用过程一览:

 

具体实现参考AbstractRefreshableApplicationContext#refreshBeanFactory实现。在这个方法中有 Bean 定义文件加载的入口:AbstractRefreshableApplicationContext#loadBeanDefinitions

 

同时有必要把资源加载接口在 Spring 体系中整理一下,有个统一的感性认识:

上面类图有两个地方需要说明一下

▪ PathMatchingResourcePatternResolver的实现

PathMatchingResourcePatternResolver 自己并不真实的执行加载资源操作,他依靠了外部传入的 ResourceLoader。如果外部没有传入,他就默认了 DefaultResourceLoader。有一点适配器模式的意思,他把 ResourceLoader 当成自己的一部分,否则他无法进行工作。

同时 PathMatchingResourcePatternResolver 中依赖了 AntPathMatcher,通过 AntPathMatcher 来处理匹配定义在资源文件名称,同时也支持了在Jar中获取资源的方式方法,如果读者感兴趣,可以查找对应的源码。

 

▪ AbstractApplicationContext的实现

AbstractApplicationContext 继承了 DefaultResourceLoader,同时实现了 ResourcePatternResolver。继承 DefaultResourceLoader,使其具有了 ResourceLoader 的服务能力,实现了 ResourcePatternResolver,使其具有了使用资源名称表达式的方式查找资源的能力。值得一提的是,他实现了 ResourcePatternResolver,并没有自己重新实现,而是让 PathMatchingResourcePatternResolver 作为真正服务的提供者。

 

2. 分析重要源码

 

2.1 AbstractXmlApplicationContext 中加载资源入口

 

还是以 FileSystemXmlApplicationContext 会使用到的实现为例来说明。参考实现代码:org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

 

// 加载Bean定义资源
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// Configure the bean definition reader with this context's
	// 资源装载的环境
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	// Application 本身就是一个资源装载器,因为他继承了DefaultResourceLoader
	beanDefinitionReader.setResourceLoader(this);
	// 定义 xml 的实体,在解析 xml 时使用
	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);
}
// 真正的装载资源入口
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);
	}
}
通过上面代码,我们了解到AbstractXmlApplicationContext并没有真正的加载Bean定义文件,他把这项艰巨的工作委托给了XmlBeanDefinitionReader。

 

2.2 XmlBeanDefinitionReader

首先看看上面构建 XmlBeanDefinitionReader 代码,他最终会使用下面代码进行实例的创建。

 

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
	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();
	}

	// Inherit Environment if possible
	if (this.registry instanceof EnvironmentCapable) {
		this.environment = ((EnvironmentCapable)this.registry).getEnvironment();
	}
	else {
		this.environment = new StandardEnvironment();
	}
}
我们知道 AbstractXmlApplication 他本身就是实现了BeanDefinitionRegistry 和 ResourceLoader 的,所以构建的 XmlBeanDefinitionReader 中的 resourceLoader registry 都是 Application 自己。

 

经过一层一层的转化和转发,最终完成使命,成功把自己的加载的任务成功转交给BeanDefinitionParserDelegate。

 

下面是重要节点源码分析:

 

▪ AbstractBeanDefinitionReader#loadBeanDefinitions(String, Set<Resource>)

 

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// AbstractXmlApplicationContext 实现了 ResourceLoader 接口,所以到运行时就是其子类的真实实例。
	ResourceLoader resourceLoader = getResourceLoader();
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException(
				"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
	}
// 所有的 Application 都是实现了 ResourcePatternResolver 接口。在前面提到过 ApplicationContext 的实现
// ResourcePatternResolver 的方式。通过这种匹配算法,可以加载多个资源文件。
	if (resourceLoader instanceof ResourcePatternResolver) {
		// Resource pattern matching available.
		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);
		}
	}
	// 如果不是 ResourcePatternResolver 只能加载一个资源文件
	else {
		// Can only load single resources by absolute URL.
		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;
	}
}
 
▪ XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)

在AbstractBeanDefinitionReader#loadBeanDefinitions(String, Set<Resource>)中调用了loadBeanDefinitions方法,会转发到这个方法。

 

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);
	}
// 把当前解析的资源加到当前线程中,如果加入失败,说明出现了循环依赖。
// 例如有资源文件bean-a.xml 引用了 bean-b.xml ,bean-b.xml 引用 bean-c.xml ,bean-c.xml 引用了 bean-a.xml
	// 他们就出现了循环依赖
	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();
		}
	}
}
▪ XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource, Resource)

这个方法是真正加载资源的地方,获取对xml进行验证的模式,以及读取xml到 Document 中。其中关于 EntityResolver 的内容会在后续的分享中详细讲解。

 

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
		int validationMode = getValidationModeForResource(resource);
// 加载资源,解析XML文件变成可用的 document 元素,方便后面使用。具体的内容请查看 Spring 源码。
		Document doc = this.documentLoader.loadDocument(
				inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
// 十分重要的方法,注册Bean定义,后面会详细讲解。
		return registerBeanDefinitions(doc, resource);
	}
	catch (BeanDefinitionStoreException ex) {
		throw ex;
	}
	catch (SAXParseException 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 parsing XML document from " + resource, ex);
	}
	catch (Throwable ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"Unexpected exception parsing XML document from " + resource, ex);
	}
}
 
▪ XmlBeanDefinitionReader#registerBeanDefinitions(Document, Resource)

下面的代码在下次分享中着重来讲

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	documentReader.setEnvironment(this.getEnvironment());
	int countBefore = getRegistry().getBeanDefinitionCount();
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

 

 

  • 大小: 47.8 KB
  • 大小: 78.6 KB
分享到:
评论

相关推荐

    spring IOC反射装载bean

    spring基于xml配置加载bean 的demo 了解spring的反射机制

    Spring Framewor开发手册

    2. Spring 2.0和 2.5的新特性 2.1. 简介 2.2. 控制反转(IoC)容器 2.2.1. 新的bean作用域 2.2.2. 更简单的XML配置 2.2.3. 可扩展的XML编写 2.2.4. Annotation(注解)驱动配置 2.2.5. 在classpath中自动搜索组件 2.3. ...

    spring学习笔记(有代码有注解解释)

    IoC 自动装载(Autowire);AOP以及如何使用; 适用人群:比较适合与我一样的在校普通大学生进行学习整理,以及适合初学spring的朋友进行巩固加深印象! 阅读建议:需要有一定的代码基础,一定的知识储备

    Java面试 spring知识点 线程池 面试题

    Spring ioc 原理 3 Bean生命周期 3 Java反射 4 beanfactory和applicationcontext 5 类装载器ClassLoader 6 Spring aop 原理 6 Aop代理 7 Spring 事物 10 数据库锁 12 ThreadLocal 13 Spring TaskExecutor线程池 16

    Spring中文帮助文档

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 ...

    spring.net中文手册在线版

    在Spring.NET内部使用资源 4.13.4.松耦合事件模型 4.13.5.IApplicationContext的事件通知 4.14.定制IApplicationContex中对象的行为 4.14.1.IApplicationContextAware标识接口 4.14.2.IObjectPostProcessor接口 4.14...

    Spring API

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 ...

    spring源代码解析

    简单的说,在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理...

    Spring.net框架

    在Step3到Step5的例子中,我们将利用Spring.net提供的Ioc框架,轻松完 成解耦以及系统改造等工作。 一、类之间的依赖 我们的第一个例子主要用于说明程序的基本构造,并且作为一个反面典型,引出为什么要解耦,以及...

    Spring.3.x企业应用开发实战(完整版).part2

    Spring3.0是Spring在积蓄了3年之久后,隆重推出的一个重大升级版本,进一步加强了Spring作为Java领域第一开源平台的翘楚地位。  Spring3.0引入了众多Java开发者翘首以盼的新功能和新特性,如OXM、校验及格式化框架...

    spring2.5.chm帮助文档(中文版)

    2.2. 控制反转(IoC)容器 2.2.1. 新的bean作用域 2.2.2. 更简单的XML配置 2.2.3. 可扩展的XML编写 2.2.4. Annotation(注解)驱动配置 2.2.5. 在classpath中自动搜索组件 2.3. 面向切面编程(AOP) 2.3.1. 更加...

    spring in action英文版

     10.2.2 装载Spring Bean至Tapestry页面中  10.3 集成JavaServer Faces  10.3.1 解析变量  10.3.2 发布请求已处理事件  10.4 集成WebWork  10.4.1 WebWork1  10.4.2 XWork/WebWork2  10.5 小...

    Spring3.x企业应用开发实战(完整版) part1

    Spring3.0是Spring在积蓄了3年之久后,隆重推出的一个重大升级版本,进一步加强了Spring作为Java领域第一开源平台的翘楚地位。  Spring3.0引入了众多Java开发者翘首以盼的新功能和新特性,如OXM、校验及格式化框架...

    Spring、SpringMVC和Mybatis框架整合包

    但与我们平时开发接触最多的估计就是IOC容器,它可以装载bean(也就是我们java中的类,当然也包括service dao里面的),有了这个机制,我们就不用在每次使用这个类的时候为它初始化,很少看到关键字new。另外spring...

Global site tag (gtag.js) - Google Analytics