Spring框架八、Spring IOC重要接口

2024-09-03 01:38

本文主要是介绍Spring框架八、Spring IOC重要接口,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们使用Spring的时候,总是定义一个applicationContext.xml的文件,然后里面会定义各种bean,那么Spring框架是怎么对这些外部资源文件访问的呢?我们下面首先来看Resource组件。

一、Resource接口

Spring 为资源访问提供了一个 Resource 接口,该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。
在这里插入图片描述

Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑,每个实现类代表一种资源访问策略。
在这里插入图片描述

  • UrlResource:访问网络资源的实现类。
  • ClassPathResource:访问类加载路径里资源的实现类。
  • FileSystemResource:访问文件系统里资源的实现类。
  • ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类。
  • InputStreamResource:访问输入流资源的实现类。
  • ByteArrayResource:访问字节数组资源的实现类。

spring访问资源的方式有了,那么spring是怎么加载资源的呢?下面我们来看ResourceLoader模块。

二、ResourceLoader接口

该组件负责对Spring资源的加载,资源指的是 xml 、 properties 等文件资源,返回一个对应类型的 Resource 对象。

/** 	定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource*/
public interface ResourceLoader {Resource getResource(String location);@NullableClassLoader getClassLoader();}

接下来我们来看一下ResourceLoader具体的实现类有哪些:
在这里插入图片描述
从上面的UML图可以看出, ResourceLoader 组件其实跟 Resource 组件差不多,都是一个根接口,对应有不同的子类实现,比如加载来自文件系统的资源,则可以使用 FileSystemResourceLoader ,加载来自 ServletContext 上下文的资源,则可以使用 ServletContextResourceLoader 。 还有最重要的一点,从上图看出, ApplicationContext , AbstractApplication 是实现了 ResourceLoader 的,这说明什么呢?说明我们的应用上下文 ApplicationContext 拥有加载资源的能力,这也说明了为什么可以通过传入一个 String resource path 给 ClassPathXmlApplicationContext(“applicationContext.xml”) 就能获得xml文件资源的原因了!

既然我们拥有了加载器 ResourceLoader ,也拥有了对资源的描述 Resource ,但是我们在xml文件中声明的标签在Spring又是怎么表示的呢?注意这里只是说对 bean 的定义,而不是说如何将转换为 bean 对象。于是就引入一个叫 BeanDefinition的组件。

三、BeanDefinition接口

BeanDefinition是一个接口,内部定义了bean对象的一些基本行为。配置文件中的标签跟我们的 BeanDefinition 是一一对应的, 元素标签拥有 class 、 scope 、 lazy-init 等配置属性, BeanDefinition 则提供了相应的 beanClass 、 scope 、 lazyInit 属性。

BeanDefinition接口部分方法:
在这里插入图片描述
BeanDefinition UML图
在这里插入图片描述
其中RootBeanDefinition 是最常用的实现类,它对应一般性的 元素标签。GenericBeanDefinition 是自 2.5 以后新加入的 bean 文件配置属性定义类,是一站式服务类。在配置文件中可以定义 父和子类 ,父用 RootBeanDefinition 表示,而子用ChildBeanDefiniton 表示,而没有父的话就使用 RootBeanDefinition 表示。AbstractBeanDefinition 对两者共同的类信息进行抽象。

Spring通过 BeanDefinition 将配置文件中的配置信息转换为容器的内部表示,并将这些 BeanDefiniton注册到BeanDefinitonRegistry中。 Spring 容器的 BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,主要是以 map 的形式保存,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。一般情况下, BeanDefinition 只在容器启动时加载并解析,除非容器刷新或重启,这些信息不会发生变化,当然如果用户有特殊的需求,也可以通过编程的方式在运行期调整 BeanDefinition 的定义。

有了加载器 ResourceLoader ,也拥有了对资源的描述 Resource ,也有了对 bean 的定义,我们不禁要问,我们的 Resource 资源是怎么转成我们的 BeanDefinition 的呢?因此就引入了 BeanDefinitionReader。

四、BeanDefinitionReader接口

BeanDefinitionReader作用是把Resource资源转成我们的 BeanDefinition。
在这里插入图片描述
在这里插入图片描述
从上面可以看出,Spring 对reader进行了抽象,具体的功能交给其子类去实现,不同的实现对应不同的类,如 PropertiedBeanDefinitionReader , XmlBeanDefinitionReader 对应从Property和xml的Resource解析成 BeanDefinition 。

我们以XmlBeanDefinitionReader为例:

XmlBeanDefinitionReader.java部分源码

	/*** 创建XmlBeanDefinitionReader需要传入一个BeanDefinitionRegistry实例*/public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {super(registry);}/*** XmlBeanDefinitionReader加载资源的入口方法*/@Overridepublic 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.isTraceEnabled()) {logger.trace("Loading XML bean definitions from " + encodedResource);}Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {// 将资源文件转为InputStream的IO流InputStream inputStream = encodedResource.getResource().getInputStream();try {// 从InputStream中得到XML的解析源InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}// 具体读取过程return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {// 关闭从Resource中得到的IO流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 {// 将XML文件转换为DOM对象,解析过程由documentLoader方法实现Document doc = doLoadDocument(inputSource, resource);// 启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则int count = registerBeanDefinitions(doc, resource);if (logger.isDebugEnabled()) {logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;}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);}}public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {//得到BeanDefinitionDocumentReader来对XML格式的BeanDefinition进行解析BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//获取容器中注册的Bean数量int countBefore = getRegistry().getBeanDefinitionCount();//加载及注册bean,具体的解析过程由实现类DefaultBeanDefinitionDocumentReader完成documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//记录本次加载的BeanDefinition个数return getRegistry().getBeanDefinitionCount() - countBefore;}

有了 BeanDefinition 后,你还必须将它们注册到工厂中去,所以当你使用 getBean() 方法时工厂才知道返回什么给你。BeanDefinitionRegistry出场。

五、BeanDefinitionRegistry接口

BeanDefinitionRegistry接口UML图:
在这里插入图片描述
从图中可以看出, BeanDefinitionRegistry 有三个默认实现,分别是 SimpleBeanDefinitionRegistry , DefaultListableBeanFactory , GenericApplicationContext ,其中 SimpleBeanDefinitionRegistry , DefaultListableBeanFactory 都持有一个Map,也就是说这两个实现类把保存了bean。而 GenericApplicationContext 则持有一个 DefaultListableBeanFactory 对象引用用于获取里边对应的Map。

DefaultListableBeanFactory.java部分源码

/*1. 整个bean加载的核心部分,是spring注册及加载bean的默认实现*/
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {//存储注册信息的BeanDefinitionprivate final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);//向Spring IOC容器注册解析的BeanDefinition@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");//检验解析的BeanDefinitionif (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {//注册的过程中需要线程同步,以保证数据的一致性// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}//检查是否已经注册过同名的BeanDefinitionif (existingDefinition != null || containsSingleton(beanName)) {//重置所有已经注册过的BeanDefinition的缓存resetBeanDefinition(beanName);}}
}

六、BeanFactory接口

BeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 来实例化、配置和管理 Bean。
BeanFactory:是IOC容器的核心接口, 它定义了IOC的基本功能,我们看到它主要定义了getBean方法。getBean方法是IOC容器获取bean对象和引发依赖注入的起点。方法的功能是返回特定的名称的Bean。
BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”。注意,BeanFactory 只能管理单例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。
BeanFactory有着庞大的继承、实现体系,有众多的子接口、实现类。来看一下BeanFactory的基本类体系结构(接口为主):
在这里插入图片描述

  1. BeanFactory作为一个主接口不继承任何接口,暂且称为一级接口。
  2. 有3个子接口继承了它,进行功能上的增强。这3个子接口称为二级接口。
  3. ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry。
  4. ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,无所不包,称为四级接口。

这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类:

  1. AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。
  2. AbstractAutowireCapableBeanFactory同样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory。
  3. DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并非抽象类。
  4. 最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更强大。

BeanFactory.java

package org.springframework.beans.factory;public interface BeanFactory {/*** 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory*/String FACTORY_BEAN_PREFIX = "&";/** 四个不同形式的getBean方法,获取实例*/Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;boolean containsBean(String name); // 是否存在boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型(多实例)boolean isTypeMatch(String name, Class<?> targetType)throws NoSuchBeanDefinitionException;// 名称、类型是否匹配Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取类型String[] getAliases(String name);// 根据实例的名字获取实例的别名

BeanFactory最常见的实现类为XmlBeanFactory,可以从classpath或文件系统等获取资源。

通过XmlBeanFactory实现启动Spring IoC容器:

public class MyTest {@Testpublic void test01() throws FileNotFoundException {
//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource resource=resolver.getResource("classpath:application_context.xml");BeanFactory beanFactory=new XmlBeanFactory(resource);BookService bookService = beanFactory.getBean("bookService", BookService.class);System.out.println(bookService);}
}
  1. XmlBeanFactory通过Resource装载Spring配置信息冰启动IoC容器,然后就可以通过factory.getBean从IoC容器中获取Bean了。
  2. 通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一个调用时。
  3. 对于单实例(singleton)的Bean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean时直接从IoC容器缓存中获取Bean。

七、ApplicationContext接口

如果说BeanFactory是Spring的心脏,那么ApplicationContext就是完整的躯体了,ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置实现。

BeanFactorty接口提供了配置框架及基本功能,但是无法支持spring的aop功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,相较于BeanFactorty,ApplicationContext还提供了以下的功能:

  • MessageSource, 提供国际化的消息访问
  • 资源访问,如URL和文件
  • 事件传播特性,即支持aop特性
  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

ApplicationContext:是IOC容器另一个重要接口, 它继承了BeanFactory的基本功能, 同时也继承了容器的高级功能,如:MessageSource(国际化资源接口)、ResourceLoader(资源加载接口)、ApplicationEventPublisher(应用事件发布接口)等。

如下UML图:

在这里插入图片描述
ApplicationContext的子接口分为两个部分:

  1. ConfigurableApplicationContext:大部分的应用上下文都实现了该接口
  2. WebApplicationContext:在web应用程序中使用

ConfigurableApplicationContext.java

void setId(String id); // 设置应用上下文唯一的id
void setParent(ApplicationContext parent); // 设置应用程序上下文的父级
void setEnvironment(ConfigurableEnvironment environment); // 设置应用上下文的环境
ConfigurableEnvironment getEnvironment();  // 获取应用上下文的环境
// 添加一个新的BeanFactoryPostProcessor
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
// 添加应用程序监听器
void addApplicationListener(ApplicationListener<?> listener);
// 添加协议解析器,可能会覆盖默认的规则
void addProtocolResolver(ProtocolResolver resolver);
// 加载或者刷新配置
void refresh() throws BeansException, IllegalStateException;
// 向JVM runtime注册一个关闭钩子,JVM关闭时关闭这个上下文
void registerShutdownHook();
// 应用程序上问下是否是激活状态
boolean isActive();
// 获取应用上下文内部的bean factory
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

AbstractApplicationContext.java

AbstractApplicationContext是ApplicationContext接口的抽象实现,这个抽象类仅仅是实现了公共的上下文特性。这个抽象类使用了模板方法设计模式,需要具体的实现类去实现这些抽象的方法。

这篇关于Spring框架八、Spring IOC重要接口的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131657

相关文章

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll