手寫一個簡單的starter組件


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組件並沒有想象中的那么復雜,知其原理再下手,只有自己有多余的時間靜下心來好好捋一捋,一點一點的積累,不斷的成長,開闊自己的眼界。
      多手寫代碼記憶深刻!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM