死磕Spring,什么是SPI机制,对SpringBoot自动装配有什么帮助

news2024/9/29 18:14:07

文章目录

  • 如果没时间看的话,在这里直接看总结
  • 一、Java SPI的概念和术语
  • 二、看看Java SPI是如何诞生的
  • 三、Java SPI应该如何应用
  • 四、从0开始,手撸一个SPI的应用实例
  • 五、SpringBoot自动装配
  • 六、Spring SPI机制与Spring Factories机制做对比
  • 七、这里是给我自己提个醒

如果没时间看的话,在这里直接看总结

1. SPI是一个机制,流程由三个组件构成

  • ServiceLoader,就是ClassLoader;
  • Service,是接口,作为文件(在META-INF/services目录下)的名称
  • ServiceProvider,是接口的实现类,作为文件(在META-INF/services目录下)的内容

2. SPI执行流程

  • ServiceLoader通过classpath路径,加载指定的Service文件,然后使用里面合适的内容ServiceProvider

一、Java SPI的概念和术语

SPI(Service Provider Interface):基于ClassLoader,发现并加载服务,机制
SPI由三个组件构成:Service、Service Provider、ServiceLoader

  • Service:是一个公开的接口或抽象类,定义了一个抽象的功能模块(文件名称)
  • Service Provider:是Service的实现类(文件内容)
  • ServiceLoader:是SPI机制中的核心组件,负责在运行时发现并加载Service Provider
    在这里插入图片描述

二、看看Java SPI是如何诞生的

  1. 在Java SPI出现之前,Class.forName()要自己根据需求写驱动类
    在这里插入图片描述

  2. JDBC要求Driver实现类在类加载的时候,能将自身的实例对象自动注册到DriverManager中,从而加载数据库驱动。
    在这里插入图片描述

  3. Java SPI逐渐融入JDBC
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

三、Java SPI应该如何应用

  1. 规范的配置文件
    在这里插入图片描述
    在这里插入图片描述
  2. Service Provider类必须具备无参的默认构造方法
    在这里插入图片描述
    在JDBC中的对应实现
    在这里插入图片描述
  3. 保证能加载到配置文件和Service Provider类
    在这里插入图片描述
    在JDBC中的对应实现
    在这里插入图片描述
    总结:上述除了导包需要自己动手以外,其他的手续都是导包之后,Java SPI自动完成的

四、从0开始,手撸一个SPI的应用实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总体流程
在这里插入图片描述

五、SpringBoot自动装配

参考视频:每一帧都是干货!15分钟的视频花2小时看
参考文章:springboot自动装配到底是什么意思?
参考文章:建立META-INF/spring.factories文件的意义何在
参考文章:springboot自动装配原理-以redis为例
参考文章:聊聊 SpringBoot 自动装配原理
参考文章:spring.factories 文件的位置

1. 手动装配Redis实例

  • 加入pom依赖
<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-redis</artifactId>
	<version>2.0.9.RELEASE</version>
</dependency>

<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.9.0</version>
</dependency>
  • 配置xml的bean的配置
 //配置连接池
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="minIdle" value="10"></property>
        <property name="maxTotal" value="20"></property>
    </bean>
    
    //配置连接工厂
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="47.104.128.12"></property>
        <property name="password" value="123456"></property>
        <property name="database" value="0"></property>
        <property name="poolConfig" ref="poolConfig"></property>
    </bean>


    //配置 redisTemplate 模版类
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory"  ref="jedisConnectionFactory"/>
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
         <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
    </bean>
  • 导入配置
    @ImportResource(locations = “classpath:beans.xml”) 可以导入xml的配置文件

2. SpringBoot自动配置Redis实例

  • 引入依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置Redis服务器
spring:
	redis:
		database:0
		host:127.0.0.1
		port:6379
		password:123456
  • 直接使用RedisTemplate或StringRedisTemplate
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
  • 提出问题:自动配置
  • 我们除了通过maven引入一个starter外,其他什么也没有做,但是呢,SpringBoot就自动完成了Redis的配置,将相关的Bean对象注册到IOC容器中了。那么SpringBoot是如何做到这一点的呢?这就是这篇博客所要说明的问题了。

2. 自动配置,一切从注解@SpringBootApplicaiton说起

  • @SpringBootApplication注解
    在这里插入图片描述
  • 下面我们逐步分析@EnableAutoConfiguration的自动配置
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
 
    Class<?>[] exclude() default {};
 
    String[] excludeName() default {};
}

AutoConfigurationImportSelector.class的selectImports方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata){
	if(!isEnabled(annotationMetadata))
		return NO_IMPORTS;
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
	//SpringBoot自动配置的入口方法
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
			autoConfigurationMetadata, annotationErtadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  • selectImports()方法中引用的getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(
		AutoConfigurationMetadata autoConfigurationMetadata,
		AnnotationMetadata annotationMetadata){
	//1. 获取annotationMetadata的注解@EnableAutoConfiguration的属性
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	//2. 从资源文件Spring.factories中获取EnableAutoConfiguration对应的所有的类
	List<String> configurations = getCandidateConfigurations(
		annotationMetadata, attributes);
	//3. 通过在注解@EnableAutoConfiguration设置exclude的相关属性,可以排除指定的自动配置类
	Set<String> exclusions = getExclusions(anntationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	//4. 根据注解@Conditional来判断是否需要排除某些自动配置类
	configurations filter = filter(configurations, autoConfigurationMetadata);
	//5. 触发AutoConfiguration导入的相关事件
	fireAutoCOnfigurationImportEvents(configurations, exclusions);
	return new AutofigurationEntry(configurations, exclusions);
}
  • getAutoConfigurationEntry()引用的getCandidateConfigurations()
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, 
		AnnotationAttributes attributes){
	//通过SpringFactories机制,从配置文件Spring.factories中找出所有的自动配置类
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
			EnableAutoConfiguration.class, getBeanClassLoader());
	Assert.notEmpty(configurations,"No auto configuration classes found");
	return configurations;
}

SpringFactoriesLoader.loadFactoryNames方法调用loadSpringFactories方法从所有的jar包中读取META-INF/spring.factories文件信息。

	// 参数:
 	// Class<?> factoryType:需要被加载的工厂类的class
 	// ClassLoader classLoader:类加载器
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			// 若没传入类加载器,使用该本类的类加载器
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// class.getName():获取该类的全类限定名字
		String factoryTypeName = factoryType.getName();
		// loadSpringFactories(classLoaderToUse) 返回是Map
		// Map.getOrDefault(A,B): A为Key,从Map中获取Value,若Value为Null,则返回B 当作返回值
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

loadSpringFactories()方法调用ClassLoader.getSystemResources()获取META-INF/spring.factories文件

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap result = (MultiValueMap)cache.get(classLoader);
        if(result != null) {
            return result;
        } else {
            try {
                Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result1 = new LinkedMultiValueMap();
 
                while(ex.hasMoreElements()) {
                    URL url = (URL)ex.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
 
                    while(var6.hasNext()) {
                        Entry entry = (Entry)var6.next();
                        List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result1.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }
 
                cache.put(classLoader, result1);
                return result1;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容,选择带有EnableAutoConfiguration自动配置类。

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
 
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
 
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

六、Spring SPI机制与Spring Factories机制做对比

  • 联系:Spring Factories自动装配借用了SPI机制,SPI机制本身就是一种思想,不是特定的技术。
  • 区别:如下
    在这里插入图片描述

七、这里是给我自己提个醒

META-IF/spring.factories是在Maven引入的Jar包中,每一个Jar都有自己META-IF/spring.factories,所以SpringBoot是去每一个Jar包里面寻找META-IF/spring.factories,而不是我的项目中存在META-IF/spring.factories(当然也可以存在,但是我项目的META-IF/spring.factories肯定没有类似以下这些东西)
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试5年,历经3轮面试成功拿下华为Offer,24K/16薪不过分吧

前言 转眼过去&#xff0c;距离读书的时候已经这么久了吗&#xff1f;&#xff0c;从18年5月本科毕业入职了一家小公司&#xff0c;到现在快5年了&#xff0c;前段时间社招想着找一个新的工作&#xff0c;前前后后花了一个多月的时间复习以及面试&#xff0c;前几天拿到了华为的…

redis(4)String字符串

前言 Redis中有5大数据类型&#xff0c;分别是字符串String、列表List、集合Set、哈希Hash、有序集合Zset&#xff0c;本篇介绍Redis的字符串String Redis字符串 String是Redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value…

Python使用百度通用API进行翻译

想汉化StarUML这个软件&#xff0c;感觉工作量太大&#xff0c;想要用Python自动翻译。 结果网上找的一个个用不了&#xff0c;或者用一会儿就断。 于是自己手写了一个简单的&#xff0c;只有两个类&#xff1a;APIConfig和Translater 使用 demo my_api_config APIConfig(…

指针的进阶——(1)

本次讲解重点&#xff1a; 1、字符指针 2、数组指针 3、指针数组 4、数组传参和指针传参 5、函数指针 关于指针这个知识点的主题&#xff0c;我们在前面已经初级阶段已经对指针有了大致的理解和应用了。我们知道了指针的概念&#xff1a; 1、指针就是地址&#xff0c;但口…

PHP基础(3)

PHP基础表单提交文件处理PHP连接数据库异常抛出表单提交 PHP通过全局变量 $_GET和 $_POST来收集表单数据。 接下来改用post方式进行提交&#xff0c;再次查看是否隐藏了提交的内容&#xff1a; 发现提交的信息已经不在链接之中进行显示了。 GET与POST区别在于一个会在连接…

番外9:使用ADS对射频功率放大器进行非线性测试1(以IMD3测试为例)

番外9&#xff1a;使用ADS对射频功率放大器进行非线性测试1&#xff08;以IMD3测试为例&#xff09; 一般可以有多种方式对射频功率放大器的非线性性能进行测试&#xff0c;包括IMD3、ACPR、ACLR等等&#xff0c;其中IMD3的实际测试较为简单方便不需要太多的仪器。那么在ADS中…

VUE的生命周期- VUE2.x

1.生命周期有哪些VUE2.x 自带八个&#xff1a;beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed2.一旦进入组件会执行哪些生命周期beforeCreate,created,beforeMount,mountedbeforeCreate,没有DOM($el),没有data&#xff0c;不能拿到方…

飞桨-鹏城云脑发行版亮相第四届启智开发者大会,软硬一体化助力科研

2月24日&#xff0c;主题为“算网筑基、开源启智、AI赋能”的第四届OpenI/O启智开发者大会在深圳开幕&#xff0c;大会由科技部指导、鹏城实验室与新⼀代人工智能产业技术创新战略联盟&#xff08;AITISA&#xff09;主办&#xff0c;科技部高新司副司长梅建平&#xff0c;中国…

Simple RNN、LSTM、GRU序列模型原理

一。循环神经网络RNN 用于处理序列数据的神经网络就叫循环神经网络。序列数据说直白点就是随时间变化的数据&#xff0c;循环神经网络它能够根据这种数据推出下文结果。RNN是通过嵌含前一时刻的状态信息实行训练的。 RNN神经网络有3个变种&#xff0c;分别为Simple RNN、LSTM、…

ESP-C3入门13. SoftAP模式

ESP-C3入门13. SoftAP模式一、 ESP32-C3 WIFI的工作模式二、SoftAP配置1. wifi_config_t 结构体2. wifi_event_handler 事件(1) esp_event_handler_instance_register 注册事件(2) system_event_sta_connected_t 结构体3. 关闭SoftAP三、示例1. main.c2. wifi_ap.h3. wifi_ap.…

自动化构建部署devops(CICD)--敏捷开发

一。gitlab结合jenkins自动化项目构建部署 代替早期的手动部署服务&#xff0c;写文档&#xff0c;java-jar启动啦。麻烦还容易出错。 二。DevOps 三。部署流水线 四&#xff0c;页面工具&#xff08;类似于ones&#xff09; 1&#xff0c;开发组长在页面添加项目成员&#…

integrationobjects点com all OPC Crack

Integration Objects 是世界领先的系统集成商和解决方案提供商&#xff0c;专门从事运营和制造智能、高级分析、异常事件的预防性检测、在线诊断和根本原因分析、OPC 连接、工厂自动化、知识管理解决方案、网络安全和企业电力和公用事业以及全球流程和制造行业的集成。 OPC UA …

storybook使用info插件报错

报错内容: RangeErrorMaximum call stack size exceededCall StackprettyPrintvendors-node_modules_pmmmwh_react-refresh-webpack-plugin_lib_runtime_RefreshUtils_js-node_mod-4ff2dd.iframe.bundle.js:160:27undefinedvendors-node_modules_pmmmwh_react-refresh-webpack-…

结构方程模型全流程

案例与数据 某研究者想要研究关于教师懈怠感的课题&#xff0c;教师懈怠感是指教师在教育情境的要求下&#xff0c;由于无法有效应对工作压力与挫折而产生的情绪低落、态度消极状态&#xff0c;这种状态甚至会引发心理、生理的困扰&#xff0c;终至对教育工作产生厌倦&#xf…

英语二-议论文写作词汇、话题、模板、范文参考

1. 词汇多样性 1. 表示因果关系 2. 表示转斩关系 3. 表示顺序关系 4. 表示递进关系 5. 表示对比关系 6. 表示总结关系 7. 连接论据的词 2. 高频考试话题 1. 有益身心的短语 2. 提高能力的短语 3. 写作模板 支持原创作文&#xff0c;如果不会&#xff0c;请牢记模板。 如果嫌…

Android源码分析 —— Activity栈管理(基于Android8)

0. 写在前面 本文基于 Android8.0源码&#xff0c;和Android9.0大同小异&#xff0c;但和Android10.0差别非常大&#xff01;新版改用ATM来管理Activity的启动&#xff0c;Activity的生命周期也通过XXXItem来管理。由于我分析的Activity启动流程就是基于Android8/9的&#xff…

FFmpeg/OpenCV 实现全屏斜体水印

实现思路 &#x1f914;​ 基于ffmpeg&#xff0c;画布的方式&#xff0c;创建画布 -> 水印 -> 旋转 -> 抠图 -> 叠加到图像上基于ffmpeg&#xff0c;旋转图片的方式&#xff0c;填充 -> 水印 -> 顺时针旋转 -> 逆时针旋转 -> 截图基于opencv&#xff…

Pag渲染过程 -- 背景知识

什么是渲染 渲染是图形程序的核心&#xff0c;无论是我们在电子设备上看到的任何图形或者文字都是利用计算机图形渲染技术给我们呈现出来的结果。在计算机里一开始是直接利用CPU往显示器的FrameBuffer内写入数据即可把图形展示到显示器上&#xff0c;但是随着用户的需求和技术…

别担心,ChatGPT还抢不动你的饭碗

前言&#xff1a; “你是谁&#xff1f;” “我是一个由OpenAI训练的大型语言模型。我旨在帮助人们解答问题和提供信息。由于我是一个计算机程序&#xff0c;所以不会感知或者思考&#xff0c;只能通过已有的数据来回答问题。如果您有任何问题&#xff0c;请随时告诉我。” ---…

rabbitmq部署安装(mac)

安装&#xff1a; // 默认已经下载了homebrew&#xff0c;更新brew资源 brew update // 执行安装 brew install rabbitmq 配置&#xff1a; // 切换到MQ目录,注意你的安装版本可能不是3.9.5&#xff08;我的版本&#xff0c;当前最新版 cd /usr/local/Cellar/rabbitmq/3.…