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中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2