自己實現一個spring-boot-starter

SpringBoot自動裝配
  1. ImportSelector
  2. Registrar
約定優於配置

SpringBoot默認會掃描META-INF下面的spring.factories文件中的類的全路徑進行裝配

SpringFramework @Conditional

Conditional.class 是從Spring4.0開始提供的,通過當前註解可以實現多條件裝配;該註解需要搭配Condition.class接口使用. 以下是註解的內容

  1. 接口
public @interface Conditional {
   Class<? extends Condition>[] value();
}

下面以接入短信服務商爲例;通過掃描我們在配置文件中配置使用哪家服務商然後自動進行裝配.

  1. 用於匹配ucloud服務商
public class UCloudConditional implements Condition {
    private Properties properties;
    @Override
    public boolean matches
		(ConditionContext conditionContext, 
		 AnnotatedTypeMetadata annotatedTypeMetadata)
		throws Exception{
        properties = new Properties();
        InputStream is = conditionContext.getResourceLoader()
			.getResource("classpath:xxx.properties").getInputStream();
            properties.load(is); // 這裏的properties文件可以自行定義
            return StringUtils.isEmpty(properties.getProperty("sms.used"))
                    ? false
                   "ucloud".equalsIgnoreCase(properties.getProperty("sms.used")); // 看當前是否匹配
  1. 用於匹配創藍服務商
	public class ClConditional implements Condition {
    private Properties properties;
// 內容差不多同上所示
    @Override
    public boolean matches
		(ConditionContext conditionContext,
		AnnotatedTypeMetadata annotatedTypeMetadata)
		throws Exception{
		
        properties = new Properties();
        InputStream  is = conditionContext.getResourceLoader().getResource("classpath:xxx.properties").getInputStream();
	properties.load(is);
	return StringUtils.isEmpty(properties.getProperty("sms.used"))
	? true
	: "cl".equalsIgnoreCase(properties.getProperty("sms.used"));
  1. 配置類
@Configuration
public class SMSMessageConfiguration {

    @Bean
    @Conditional(UCloudConditional.class)
    public ISendMessageTemplate createUCloudTemplate() {
        return new UCloudSendMessageTemplate();
    }

    @Bean
    @Conditional(ClConditional.class)
    public ISendMessageTemplate createClTemplate() {
        return new ClSendMessageTemplate();
    }
}
  1. 提供服務
public interface ISendMessageTemplate {
    void sendMessage(String phone, String content);
}

public class ClSendMessageTemplate {
 void sendMessage(String phone, String content){
	System.out.printf("using chuanglan send msg, phone:%s, content:%s", phone, content);
 }
}

public class UCloudSendMessageTemplate {
	void sendMessage(String phone, String content){
	   System.out.printf("using ucloud send msg, phone:%s, content:%s", phone, content);
	}
}
public class SendMessageTester {
	@Autowired
    private ISendMessageTemplate sendMessageTemplate;

    /**
     * 短信發送
     *
     * @param phone
     * @param content
     * @return
     */
	@Test
    public void testSend(){
        sendMessageTemplate.sendMessage("13800138000", "Hi");
    }
}
SpringBoot @ConditionXXX註解

通過如上案例會發現還是需要很多的代碼才能實現一個條件注入;因此在SpringBoot中對於上述進行了進一步的簡化,SpringBoot中提供了大量的@ConditionXxx註解. 例如提供了諸如: @ConditionalOnBean@ConditionalOnClass@ConditionalOnProperty@ConditionalOnMissingBean等註解的支持. 還是以上述的發送短信案例爲例,依據上面的代碼做如下改動

  1. configuration
@Configuration
public class SMSMessageConfiguration {
    @Bean
    @ConditionalOnProperty(name = "sms.used", havingValue  = "ucloud")
    public ISendMessageTemplate createUCloudTemplate() {
        return new UCloudSendMessageTemplate();
    }
	
    @Bean
    @ConditionalOnProperty(value  = "sms.used", havingValue  = "cl")
    public ISendMessageTemplate createClTemplate() {
        return new ClSendMessageTemplate();
    }
}
  1. 測試
@SpringBootTest
public class SendMessageTester {

    @Autowired
    private ISendMessageTemplate sendMessageTemplate;

    @Test
    public void testSend(){
        sendMessageTemplate.sendMessage("13800138000", "Hi");
    }
}

通過上訴案例兩者都可以實現條件裝配,相比於SpringFramework框架提供的方式,SpringBoot更方便,提供的條件裝配的註解種類更多

手寫個starter(以Redis爲例)
  1. 依賴
	 <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.3.1</version>
        </dependency>
  1. properties
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {

    private String  host = "localhost";

    private Integer port = 6379;

    private Integer timeout = 10000;

    private boolean ssl;

    private String password;

}
  1. configuration
// 根據前面的內容我們知道
// 僅當Redisson.class被使用到時才裝配
@ConditionalOnClass(Redisson.class)
// 激活配置
@EnableConfigurationProperties(RedissonProperties.class)
@Configuration
public class RedissonAutoConfiguration {

    // 這裏是配置
    @Bean
    public RedissonClient newRedissonClient(RedissonProperties redissonProperties) {
        Config config = new Config();

        config.useSingleServer()
                .setAddress(redissonProperties.getHost()
                        + ":"
                        + redissonProperties.getPort())
                .setPassword(redissonProperties.getPassword())
                .setConnectTimeout(redissonProperties.getTimeout());

        return Redisson.create(config);

    }
}
  1. spring.factories (MATA-INF)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.bellamy.learn.redisson.configuration.RedissonAutoConfiguration
開啓提醒功能
  1. MAVEN依賴
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<version>2.3.3.RELEASE</version>
</dependency>
  1. 增加提示的配置內容

additional-spring-configuration-metadata.json(位置同上)

{
  "properties": [
    {
      "name": "redisson.host",
      "type": "java.lang.String",
      "description": "redis服務地址.",
      "defaultValue": "localhost"
    },
    {
      "name": "redisson.prot",
      "type": "java.lang.Integer",
      "description": "redis服務端口號.",
      "defaultValue": 6379
    },
    {
      "name": "redisson.timeout",
      "type": "java.lang.Integer",
      "description": "redis服務連接超時時間.",
      "defaultValue": 10000
    },
    {
      "name": "redisson.ssl",
      "type": "java.lang.Boolean",
      "description": "ssl連接.",
      "defaultValue": false
    },
    {
      "name": "redisson.password",
      "type": "java.lang.String",
      "description": "redis密碼.",
      "defaultValue": ""
    }
  ]
}
在測試工程中引入如上的項目依賴
  1. 依賴
<dependency>
	<groupId>org.bellamy.learn</groupId>
	<artifactId>redis-spring-boot-starter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>
	redisson.host=127.0.0.1
	redisson.port =6379
	redisson.timeout=60000
  1. 開始測試
@RestController
public class TestController {

    @Autowired
    private RedissonClient client;

    @GetMapping(value = "/operation")
    public Object operation(String val) throws ExecutionException, InterruptedException {
        RBucket<String> bucket = client.getBucket("name");
        if (bucket.get() == null) {
            bucket.set(val);
        }
        return bucket.get();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章