在實際項目開發(fā)中,我們常常會用到各種各樣的starter空民,為什么我們引入這些starter依賴就能夠快速的使用它們提供的功能刃唐,其中到底有什么奧秘,它們的實現(xiàn)原理是什么界轩,本節(jié)內(nèi)容就給大家演示一下如何自己編寫spring-boot-starter-redis画饥。
一、新建一個maven項目spring-boot-starter-redis
引入如下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.5.7.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
二浊猾、在此項目中編寫RedisProperties.class抖甘,用以從application.properties中讀取redis的配置信息倦挂。
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
private String host;
private Integer port;
//getter/setter省略...
}
三孝治、在此項目中編寫RedisAutoConfiguration.class胞谈,用以將Jedis的bean裝載進spring容器中
@SpringBootConfiguration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(Jedis.class)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(Jedis.class)
public Jedis jedis(RedisProperties redisProperties) {
//spring會自動將RedisProperties這個bean注入進來填具,讀者也可以手動注入
return new Jedis(redisProperties.getHost(), redisProperties.getPort());
}
}
為大家解釋一下這些注解的含義
- @SpringBootConfiguration:代表這是一個配置類
- @EnableConfigurationProperties(RedisProperties.class):將RedisProperties加載到spring容器中。參考Spring-Boot之@Enable*注解的工作原理
- @ConditionalOnClass(Jedis.class):當(dāng)項目引用了Jedis的jar時艰额,才加載此配置類澄港。參考Spring-Boot autoconfigure之Condition
重點來了
作者在編寫RedisProperties.class
這個類的時候遇到了一些問題,IDEA提示如下:
點擊右上角打開spring官方文檔查看柄沮,說需要我在pom.xml文件中引入spring-boot-configuration-processor依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>1.5.7.RELEASE</version>
<optional>true</optional>
</dependency>
本以為問題解決回梧,沒想到又提示我:
這下把我給整蒙了,找了好久的資料祖搓,最后無意間把問題解決了 - -狱意!
四、在此項目中resource目錄下新建application.properties文件
redis.host=127.0.0.1
redis.port=6379
好棕硫,接下來開始我們的另一個項目髓涯,去使用我們自己編寫的spring-boot-starter-redis
五、新建Blog項目哈扮,引入spring-boot-starter-redis依賴
<dependency>
<groupId>com.bamu.jianshu</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
六、在Blog項目中蚓再,編寫啟動類BlogApplication
@SpringBootApplication
public class BlogApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BlogApplication.class, args);
Jedis jedis = context.getBean(Jedis.class);
//如果成功連接上了redis滑肉,jedis.ping()會返回一個pong
System.out.println(jedis.ping());
}
}
七、本地啟動redis
啟動redis的過程不再贅述
好摘仅,我們現(xiàn)在可以試試看靶庙,運行Blog項目的啟動類,但是肯定會失敗的娃属。原因在于我們的Blog項目獲取不到spring-boot-starter-redis項目中的Jedis這個bean六荒。參考Spring-boot @EnableAutoConfiguration源碼分析。這篇文章講述了一種方式矾端,事實上還有另一種方式掏击,編寫一個EnableRedis的注解,使用@Import將RedisAutoConfiguration這個類給導(dǎo)入進去秩铆。
1)方式1 在Blog項目中編寫@EnableRedis注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RedisAutoConfiguration.class)
public @interface EnableRedis {
}
將此注解加在BlogApplication.class啟動類上
@SpringBootApplication
@EnableRedis//關(guān)鍵的一步
public class BlogApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BlogApplication.class, args);
Jedis jedis = context.getBean(Jedis.class);
System.out.println(jedis.ping());
}
}
2)方式2 在Blog項目中resource的META-INF目錄下寫一個spring.factoryes配置文件砚亭,加載RedisAutoConfiguration.class
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.jianshu.bean.RedisAutoConfiguration
好,所有代碼編寫完畢殴玛。我們可以試一試捅膘,可是!9鏊凇寻仗!又報錯了,NullPointerException凡壤,我們獲取到的RedisProperties里面的Host/port字段都會null署尤,為什么蔬咬??沐寺?難道@ConfigurationProperties注解從環(huán)境中沒有拿到我這兩個字段林艘。
這個問題把我給耽誤了好幾個小時。這里要著重講一講混坞,也是給我自己提個醒狐援。錯誤的關(guān)鍵點在于編寫了host和port這兩個字段的默認(rèn)值的application.properties文件不應(yīng)寫在spring-boot-starter-redis這個項目中,而應(yīng)寫在Blog項目中究孕。
在更換了application.properties文件的位置后啥酱,兩種方式都能運行成功。
為什么需要寫在引用starter的項目中厨诸,我的理解是:ConfigurationProperties從環(huán)境Environment中獲取字段默認(rèn)值镶殷,我們啟動的是Blog項目,環(huán)境加載的是Blog項目的application.properties文件微酬。所以我們應(yīng)該把配置寫在Blog項目中绘趋。
其實從結(jié)論倒推原因我們也可以理解,假如我們的項目需要用到某一個starter颗管,例如spring-boot-starter-mongo陷遮,難道我還得從starter-mongo的代碼中去修改host/port/password等等參數(shù)嗎?必然是在我們自己項目的application.properties文件中配置參數(shù)垦江,就可以直接獲取到Bean帽馋!
最后,講一個擴展點:其實spring-boot-autoconfiguration.jar中已經(jīng)為我們集成了大量的第三方中間件:redis比吭、mongo绽族、kafka等。本文講述的實現(xiàn)方式是源碼中redis的實現(xiàn)方式的簡化版衩藤,源碼作者也編寫了兩個類RedisProperties吧慢、RedisAutoConfiguration,讀者可以去一探究竟慷彤,試試看能否通過些許配置娄蔼,拿到redisTemplate這個對象,用以在生產(chǎn)級別來使用底哗。具體實現(xiàn)方式會在接下來的文章中講解岁诉。讀者可以持續(xù)關(guān)注我的springboot系列文章!