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

相关文章

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.