`
游伯度
  • 浏览: 22837 次
  • 性别: 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 IOC反射装载bean"的主题中,我们将深入探讨Spring如何通过XML配置文件来加载和管理Bean,并利用Java的反射机制来实例化这些Bean。 首先,Spring容器通过读取XML配置文件来获取Bean的定义。这些...

    Spring3.0.5所有jar包及每个jar包作用说明文档

    1. **spring-core.jar**:这是Spring框架的核心库,包含了IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)的基础实现。它提供了一些基本的工具类,如BeanFactory和ApplicationContext...

    springboot 3.0.2 自动配置(auto-configuration) 启动器(starter)保姆级教程带源码

    Spring Boot 3.0.2 自动配置(auto-configuration)和启动器(starter)是其核心特性,旨在简化Spring应用程序的开发过程。本篇保姆级教程将带你深入理解这两个概念,结合源码分析,帮助你快速上手。 **自动配置...

    spring 所有jar包

    3. **spring-context**: 在核心和bean模块的基础上,提供了上下文容器,支持AOP(面向切面编程)、事件传播、国际化和资源加载等功能。 4. **spring-aop**: 提供了面向切面编程的支持,可以方便地实现拦截器、通知等...

    spring学习需要的常用jar包

    11. **spring-instrument.jar**:提供了类装载器级别的工具,用于服务器环境中的类增强。 在学习Spring时,你可能会遇到如何配置这些JAR包,如何创建Bean,如何使用AOP进行事务管理,以及如何使用Spring MVC来开发...

    Spring框架的所有jar包

    1. **spring-core.jar**:这是Spring框架的基础,提供了核心工具类,包括IoC(Inversion of Control,控制反转)容器的基础,如BeanFactory和ApplicationContext。它还包含了资源访问、事件传播以及与第三方库的集成...

    单独使用Spring 3.1时要加的所有包

    2. **spring-context**: 扩展了`spring-core`,提供了一个更丰富的上下文环境,包括事件传播、国际化、资源加载和AOP(Aspect-Oriented Programming,面向切面编程)支持。此外,它还包含了用于集成其他框架的模块,...

    spring入门学习-5、AOP概念及特点.pdf

    它主要侧重于通过 Spring IoC 容器实现 AOP 功能,并且支持基于 XML 和注解的方式配置 AOP。Spring AOP 的特点包括: - **支持方法级别的连接点**:只支持方法级别的切面。 - **灵活的通知类型**:提供了多种不同...

    Spring试题和答案-迅捷PDF转换器.docx

    - **轻量级**:Spring 框架的设计原则之一就是保持轻量化,这意味着它对系统的资源消耗较小,启动速度较快,并且易于部署。 - **IOC(Inversion of Control,控制反转)**:Spring 通过 IoC 容器实现了依赖注入的...

    Spring全家桶知识笔记.pdf

    Spring的IoC容器在这些基础上提供了更多高级服务,比如bean的实例缓存、生命周期管理、代理、事件发布和资源加载等。 Spring的高层容器视图展示了容器启动时如何读取应用提供的配置信息,生成bean配置注册表,然后...

    Spring_3.x企业应用开发实战 目录

    - **IoC概念**:解释 IoC(Inversion of Control)的概念及其在 Spring 中的作用。 - **IoC的类型**:讨论不同类型的 IoC 容器。 - **通过容器完成依赖关系的注入**:介绍如何使用容器自动注入依赖关系。 - **...

    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学习总结笔记

    Spring框架是Java开发中最常用的轻量级框架之一,它的核心特性是依赖注入(Dependency Injection,简称DI),通过IOC(Inversion of Control,控制反转)容器管理对象的生命周期和对象间的依赖关系。本篇笔记将详细...

    spring笔试题28题选择+2简答

    - **自动装载**: Spring可以根据依赖关系自动匹配并注入相应的Bean。 **知识点2:构造方法注入的特点** - **优点**: 可以确保依赖关系的完整性,特别适用于不可变对象或者必需的依赖关系。 - **缺点**: 当依赖关系...

    Spring教材

    Spring作为Java企业级应用开发的重要框架,其核心特性包括控制反转(IoC)和面向切面编程(AOP)。以下是对这两个主要特性的详细阐述。 1. 控制反转(Inversion of Control, IoC) IoC是Spring的核心概念之一,它...

    SpringMvc依赖完整jar

    - `spring-core.jar`:提供 IoC(Inversion of Control,控制反转)和 DI(Dependency Injection,依赖注入)的核心实现,是整个 Spring 框架的基础。 - `spring-beans.jar`:包含 Bean 定义和管理,以及相关配置...

    Spring框架学习教程(一)

    BeanFactory提供了工厂模式的经典实现,而ApplicationContext作为BeanFactory的子接口,提供了更丰富的框架功能,如国际化支持、事件传播、资源装载等。 在学习Spring时,开发者需要能够编写入门案例,包括配置需要...

    spring学习手册

    - **IOC & DI概述**:介绍了IOC和DI的基本概念及其在Spring中的应用。 - **配置Bean**:包括基于XML文件的方式和基于注解的方式。 - **Bean配置方式**:可以通过全类名、工厂方法等方式进行配置。 - **IOC容器**:...

    关于spring boot中几种注入方法的一些个人看法

    @Autowired 的工作原理是,在启动 Spring IoC 时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,当容器扫描到 @Autowired、@Resource 或 @Inject 时,就会在 IoC 容器自动查找需要的 bean...

Global site tag (gtag.js) - Google Analytics