spring-boot中有很多第三方包,都封装成starter组件,在maven中引用后,启动springBoot项目时会自动装配到spring ioc容器中。
思考:
为什么我们springBoot中maven引用starter组件,就可以直接去ioc容易中拿到?
猜想是不是springBoot启动去加载这些starter组件?
那么具体是怎么实现呢?
猜想是不是所有的starter组件包都是按照一定的格式约束存在于某个文件夹下,然后springBoot启动就会去自动装配呢?
如果了解了springBoot的自动装配,那么我们如何去写一个简单的starter组件呢?
带着这些疑问我们继续往下走........
自动装配的简单说明:
在selectImports方法中,它会批量扫描在META-INF/spring.factories的文件,遍历把spring.factories中的key和value放入内置的缓存MultiValueMap中。
然后经过移除、过滤一些配置,最后加载到spring ioc容器中。原码就不具体讲了,大家可以具体去猜想下,然后再去debug看。
备注:有的组件starter命名是在前,有的是在后,比如mybatis-spring-boot-starter,spring-boot-starter-web,一种代表第三方,一种代表spring官方的,
两者并没有什么区别。
了解完springBoot的自动装配后,接下来我们按照这样的模式去手写starter组件,准备idea和安装redis。
流程图:
项目工程图:
redisson-spring-boot-starter工程:
- RedissonAutoConfiguration 类
package com.sqp.example; import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* @author sqp
* @date 2020/7/27 10:29
*/
@Configuration
// 条件装配,用于控制bean是否被加载
@ConditionalOnClass(Redisson.class)
// RedissonProperties注入到spring ioc容器中
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration { /**
* 下面方法引用RedissonProperties说明其已经注入到spring ioc容器中,@EnableConfigurationProperties会加载
* @param redissonProperties
* @return
*/
@Bean
RedissonClient redissonClient(RedissonProperties redissonProperties){
Config config = new Config();
String prefix = "redis://";
if(redissonProperties.isSsl()){
prefix = "rediss://";
}
config.useSingleServer().setAddress(prefix+redissonProperties.getHost()+":"+redissonProperties.getPort()).setConnectTimeout(redissonProperties.getTimeout());
return Redisson.create(config);
}
}
- RedissonProperties
/**
* 绑定配置
* @author sqp
* @date 2020/7/27 10:40
*/
@Setter
@Getter
@ConfigurationProperties(prefix = "sqp.redisson")
public class RedissonProperties { private String host = "localhost";
private int port = 6379;
private int timeout;// 超时时间
private boolean ssl;
}
- 配置文件spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration =\
com.sqp.example.RedissonAutoConfiguration
- pom文件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.6.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.2</version>
</dependency>
</dependencies>
spring-boot-demo工程:
- RedissonController
package com.sqp.example.springbootdemo; import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* @author sqp
* @date 2020/7/27 11:40
*/
@RestController
@RequestMapping
public class RedissonController { @Autowired
private RedissonClient redissonClient; @GetMapping("/test")
public String test(){
RBucket bucket = redissonClient.getBucket("name");
if(bucket.get() == null){
bucket.set("com.sqp.redisson");
}
return bucket.get()+"";
}
}
- application.properties配置
sqp.redisson.host=120.77.2.12
sqp.redisson.port=6379
- pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.1.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sqp.example</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
项目构建完成启动
当我们springBoot启动源码中可以看到我们自己写的这个类会自动被加载到spring ioc中,大家可以自己试试。
测试结果:
我们在redis服务器上看到key为name,value为com.sqp.redisson,测试结果没有问题。
备注:这里的value是经过序列化的。
疑问:虽然测试得到了我们想要的,但是在spring-boot-demo中的配置文件中存在一个问题,没有自动提示。
解决:
1、在项目redisson-spring-boot-starter-->resources--->META-INF---->新建additional-spring-configuration-metadata.json文件,格式如下:
{
"properties": [
{
"defaultValue": "localhost",
"name": "sqp.redisson.host",
"description": "redis server address",
"type": "java.lang.String"
},
{
"defaultValue": 6397,
"name": "sqp.redisson.port",
"description": "redis server port",
"type": "java.lang.Integer"
}
]
}
2、redisson-spring-boot-starter项目的pom文件添加依赖包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
3、把redisson-spring-boot-starter重新打包下 ,看下是否有生成spring-configuration-metadata.json。
自动提示如下:
总结:你会发现其实写starter组件并没有想象中的那么复杂,知其原理再下手,只有自己有多余的时间静下心来好好捋一捋,一点一点的积累,不断的成长,开阔自己的眼界。
多手写代码记忆深刻!
手写一个简单的starter组件的更多相关文章
-
利用SpringBoot+Logback手写一个简单的链路追踪
目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...
-
带着萌新看springboot源码13(手写一个自己的starter)
springboot的最强大的就是那些xxxAutoconfiguration,但是这些xxxAutoConfiguration又依赖那些starter,只有导入了这些场景启动器(starter),我 ...
-
手写一个简单的ElasticSearch SQL转换器(一)
一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...
-
手写一个简单到SpirngMVC框架
spring对于java程序员来说,无疑就是吃饭到筷子.在每次编程工作到时候,我们几乎都离不开它,相信无论过去,还是现在或是未来到一段时间,它仍会扮演着重要到角色.自己对spring有一定的自我见解, ...
-
手写一个简单的HashMap
HashMap简介 HashMap是Java中一中非常常用的数据结构,也基本是面试中的"必考题".它实现了基于"K-V"形式的键值对的高效存取.JDK1.7之前 ...
-
jquery 手写一个简单浮窗的反面教材
前言 初学jquery写的代码,陈年往事回忆一下. 正文 介绍一下大体思路 思路: 1.需要控制一块区域,这块区域一开始是隐藏的. 2.这个区域需要关闭按钮,同时我需要写绑定事件,关闭的时候让这块区域 ...
-
手写一个简单版的SpringMVC
一 写在前面 这是自己实现一个简单的具有SpringMVC功能的小Demo,主要实现效果是; 自己定义的实现效果是通过浏览器地址传一个name参数,打印“my name is”+name参数.不使用S ...
-
socket手写一个简单的web服务端
直接进入正题吧,下面的代码都是我在pycharm中写好,再粘贴上来的 import socket server = socket.socket() server.bind(('127.0.0.1', ...
-
如何手写一个简单的LinkedList
这是我写的第三个集合类了,也是简单的实现了一下基本功能,这次带来的是LinkedList的写法,需要注意的内容有以下几点: 1.LinkedList是由链表构成的,链表的核心即使data,前驱,后继 ...
随机推荐
-
改用二进制启动Moses translation model提示Can&#39;t read ~/working/binarised-model/reordering-table
解决方案: 换成 /home/用户名/working 貌似就好使了...但是时间还是估计太长,明早挂机一天试试,顺便把manual 的详细部分看了
-
macOS10.12允许所有来源设置
如何调出允许所有来源呢? 很简单一行命令搞定 调出允许所有来源 1.打开终端执行命令 sudo spctl --master-disable 2.你在打开偏好设置--> 安全与隐私 好了赶快 ...
-
(转载)php如何判断IP为有效IP地址
(转载)http://www.kuitao8.com/20130918/1376.shtml 多数人看到这篇日志,第一印象肯定是以为是要讲如何通过正则表达式来判断. 非也,在php5.2.0之后,有专 ...
-
VMware 虚拟机(linux)增加根目录磁盘空间
今天查看学校的监控报修系统,不能访问了!!!系统运行很慢,用top命令查看发现内存使用率90%,用"df -h ”查看“/”目录使用率已达到80%,导致系统运行很慢.我用以下方法扩大根目录磁 ...
-
Android官方命令深入分析之AVD Manager
作者:宋志辉 AVD Manager提供了一个图形用户接口,通过它你能够创建和管理AVDs. 你能够通过下面方式执行AVD Manager: Eclipse:选择 Window > Androi ...
-
linux系统常见压缩命令
在linux环境中,压缩文件的扩展名基本是:*.tar,*.tar.gz,*.tgz,*.gz,*.Z,*.bz2 *.Z compress程序压缩的文件 *.gz gzip程序压缩的文件 *.bz2 ...
-
selenium之 webdriver与三大浏览器版本映射表(更新至v2.29)
1.chrome浏览器 chromedriver版本 支持的Chrome版本 v2.29 v56-58 v2.28 v55-57 v2.27 v54-56 v2.26 v53-55 v2.25 v53 ...
-
2017-2018-2 20155309南皓芯 Exp8 WEB基础实践
基础问题回答 (1)什么是表单 表单在网页中主要负责数据采集功能.一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法. 表单域:包含了文本 ...
-
解题:AT2064 Many Easy Problems&;EXNR #1 T3 两开花
题面 两道题比较像,放在一起写了,后者可以看成前者的加强版 (sto ztb orz) 先看AT那道题 考虑计算每个点的贡献,用容斥计算:每个点没有贡献当且仅当选的所有点都在以他为根时的一个子节点的子 ...
-
Java的HashMap
FAQ: 为什么要有HashMap? 答:我非常期待能在Java 中使用Hash表 这种数据结构 ,因为它的快速存取特性. Hash表 和HashMap的关系? 答:Hash表 是一种逻辑数据结构,H ...