自定義Starter:
Starter會把所有用到的依賴都給包含進來,避免了開發者自己去引入依賴所帶來的麻煩。Starter 提供了一種開箱即用的理念,其中核心就是springboot的自動配置原理相關,調用相關的注解實現條件判斷,進而進行Bean的注入管理。
基於SpringBoot自動配置原理的學習,現在我們來實現一下自定義starter。我們假設有一個格式化的,暫定兩個實現,一個是Object-> String,一個是Object->JSON.
1.新建一個maven工程 format-spring-boot-starter,這里的命名在SpringBoot官網有建議:For example, assume that you are creating a starter for "acme" and that you name the auto-configure module acme-spring-boot-autoconfigure
and the starter acme-spring-boot-starter
. If you only have one module that combines the two, name it acme-spring-boot-starter. 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> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.56</version> <optional>true</optional><!--可選--> </dependency>
<!--支持ConfigurationProperties--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>2.1.6.RELEASE</version> <optional>true</optional> </dependency> </dependencies>
2.構建格式化接口並且定義實現,咱們這里想實現動態注入的想過,定義兩個實現以做對比:
public interface FormatProcessor { //定義一個格式化的方法
<T> String format(T obj); } //實現1 JSON
public class JsonFormatProcessor implements FormatProcessor{ @Override public <T> String format(T obj) { return "JsonFormatProcessor:"+ JSON.toJSONString(obj); } } //實現2 String
public class StringFormatProcessor implements FormatProcessor{ @Override public <T> String format(T obj) { return "StringFormatProcessor:"+Objects.toString(obj); } }
3.這里我們需要把這兩個類根據 Conditional 注解動態注入到Spring 容器中,在springboot自動配置中我們說到有個 metadata 文件可以做過濾,而這里的 ConditionalOnClass 是一樣的,只是方式不一樣:
@Configuration public class FormatAutoConfiguration {
//metadata-auto....
@ConditionalOnMissingClass("com.alibaba.fastjson.JSON") @Bean @Primary public FormatProcessor stringFormat(){ return new StringFormatProcessor(); } @ConditionalOnClass(name = "com.alibaba.fastjson.JSON") @Bean public FormatProcessor jsonFormat(){ return new JsonFormatProcessor(); } }
4.我們這里參照着 jdbcTemplate 的模板來實現我們這個需求:
public class HelloFormatTemplate { private FormatProcessor formatProcessor;public HelloFormatTemplate(FormatProcessor formatProcessor) {this.formatProcessor = formatProcessor; } //定義一個格式化方法,具體的實現由FormatProcessor實現類實現 public <T> String doFormat(T obj){ StringBuilder stringBuilder=new StringBuilder(); stringBuilder.append("begin:Execute format").append("<br/>"); stringBuilder.append("Obj format result:").append(formatProcessor.format(obj)).append("<br/>"); return stringBuilder.toString(); } }
5.然后我們需要動態的注入:
@Import(FormatAutoConfiguration.class)
@Configuration public class HelloAutoConfiguration { @Bean public HelloFormatTemplate helloFormatTemplate(FormatProcessor formatProcessor){ return new HelloFormatTemplate(formatProcessor); } }
6.接下去需要在classpath下的META-INF文件夾下創建一個文件 spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.wuzz.starter.autoconfiguration.HelloAutoConfiguration
然后將該工程打成Jar,然后我們需要做的是在我們需要用到這個Starter的boot項目重中導入該starter
1.導入starter,我們會看到有些starter里面是空的,其實他做的僅僅是依賴傳遞,這樣容器中存在某個類,這個時候符合另外一個類的加載,也能起到開箱即用的效果:
<dependency>
<groupId>com.wuzz.starter</groupId>
<artifactId>format-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.定義一個實體類如下:
public class User { private String name; private int age; //省略 get set @Override public String toString() { return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}'; } }
3.我們寫一個controller來測試效果:
@RestController public class FormatController { @Autowired HelloFormatTemplate helloFormatTemplate; @GetMapping("/format") public String format(){ User user=new User(); user.setAge(18); user.setName("wuzz-starter"); return helloFormatTemplate.doFormat(user); } }
其他無需任何配置,我們直接啟動該項目,通過 localhost:8080/format 來訪問會看到以下效果:
再來,我們導入以下依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
重啟項目再去訪問:
是不是很爽?這樣子就實現了Starter的自定義,
SpringBoot外部配置:
這個時候我們還存在另外一個問題沒有解決,就類似於我們導入了Eureka的starter,那么這個時候我們可以在 application.properties 中可以配置很多的信息,這個就是springboot的外部配置,取決於注解 @ConfigurationProperties。
我們是不是也能實現這樣的效果呢?答案是當然咯,那么接下去我們就基於原來的工程進行再一次的改造。
1.增加我們自定義的Properties 類:
@ConfigurationProperties(prefix="wuzz.hello.format") public class HelloProperties { private String appOwner; private Map<String,Object> info; public String getAppOwner() { return appOwner; } public void setAppOwner(String appOwner) { this.appOwner = appOwner; } public Map<String, Object> getInfo() { return info; } public void setInfo(Map<String, Object> info) { this.info = info; } }
2.修改HelloTemplate 如下:
public class HelloFormatTemplate { private FormatProcessor formatProcessor; private HelloProperties helloProperties; public HelloFormatTemplate(HelloProperties helloProperties, FormatProcessor formatProcessor) { this.helloProperties = helloProperties; this.formatProcessor = formatProcessor; } public <T> String doFormat(T obj) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("begin:Execute format").append("<br/>"); stringBuilder.append("HelloProperties:").append(helloProperties.getAppOwner()).append("<br/>") .append(formatProcessor.format(helloProperties.getInfo())).append("<br/>"); stringBuilder.append("Obj format result:").append(formatProcessor.format(obj)).append("<br/>"); return stringBuilder.toString(); } }
3.修改HelloAutoConfiguration如下:
@Import(FormatAutoConfiguration.class) @EnableConfigurationProperties(HelloProperties.class) @Configuration public class HelloAutoConfiguration { @Bean public HelloFormatTemplate helloFormatTemplate(HelloProperties helloProperties,FormatProcessor formatProcessor){ return new HelloFormatTemplate(helloProperties,formatProcessor); } }
然后我們重新打個包,再在需要依賴的那個工程中更新導入一下maven。然后我們在application.properties中就可以實現我們的配置:
配置兩條配置項:wuzz.hello.format.app-owner=wuzz ,wuzz.hello.format.info.app=starter-demo
然后啟動項目我們再次訪問:
效果非常的好,在我們需要自定義的封裝一些自定義組件的時候就可以使用這個拓展啦。