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组件并没有想象中的那么复杂,知其原理再下手,只有自己有多余的时间静下心来好好捋一捋,一点一点的积累,不断的成长,开阔自己的眼界。
多手写代码记忆深刻!