目錄
方式一:使用spring.factories文件,通過配置enable實現開關
https://github.com/HandsomeMars/springboot-autoconfig-demo
一 前言
1、今天想寫一個自動配置的模塊,參考了一系列文章,感覺非常失望。
問題如下:
1、混淆了自動配置註解:@EanbleXXX註解 加與不加都會生效配置 其實現關鍵是自動配置上通過其他條件加載完成
2、無效代碼:太多的模仿代碼,讓人云裏霧裏;以及不完整測試,只測開啓不測關閉
2、自己看了springboot官方文檔,寫了兩種形式的自動配置
環境 | 版本 |
springboot | 2.0.3.release |
maven | 3.5.2 |
jdk | 1.8 |
二 實現原理
(1)springboot啓動類註解解析
springboot啓動類中@SpringBootApplication
包含:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其中@EnableAutoConfiguration
包含:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
其中
AutoConfigurationImportSelector
實現了從所有包下的 META-INF/spring.factories
讀取自動配置類信息,通過類加載初始化所描述的bean
由上即可知自動配置的最簡單的實現方式
1、新建META-INFO/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zyj.config.Custom2AutoConfig #需要被初始化的類
2、查詢api可知,等效寫法:實現如下接口,通過@Import等方式初始化
org.springframework.context.annotation.ImportSelector
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//邏輯判斷 返回對應的配置類包全路徑
if(annotationMetadata.getAnnotationTypes().contains(EnableCustomAutoConfig.class.getName())){
System.out.println("沒錯你是最棒的");
return new String[]{CustomAutoConfig.class.getName()};
}else{
return new String[0];
}
}
(2)參考
三 實現
方式一:使用spring.factories文件,通過配置enable實現開關
1、目錄結構
─src
├─main
│ ├─java
│ │ └─com
│ │ └─zyj
│ │ │ SpringbootAutoconfigByfileApplication.java ##可忽略
│ │ │
│ │ └─config
│ │ Custom2AutoConfig.java ##關鍵配置類
│ │ Custom2Properties.java ##參數配置類
│ │ Custom2Server.java ##模擬服務(可忽略)
│ │
│ └─resources
│ │ application.properties ##可忽略
│ │
│ └─META-INF
│ spring.factories ##關鍵配置文件,指定初始化關鍵配置類
│
└─test
└─java
└─com
└─zyj
SpringbootAutoconfigByfileApplicationTests.java ##可忽略
2、關鍵代碼
pom依賴
<!--關鍵依賴 @ConfigurationProperties-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--更加自動配置框架而定-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Custom2Properties.java
package com.zyj.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:35
*/
/**解析spring配置文件中custom2開頭的配置信息*/
@ConfigurationProperties(prefix="custom2")
public class Custom2Properties {
private boolean enable;
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("[");
stringBuilder.append("enable:"+enable);
stringBuilder.append(",");
stringBuilder.append("test:"+test);
stringBuilder.append("]");
return stringBuilder.toString();
}
}
Custom2AutoConfig.java
package com.zyj.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:29
*/
/**類似@Import 加載屬性文件(相當於屬性文件@Component)*/
/**與下面@Autowired配合*/
@EnableConfigurationProperties(Custom2Properties.class)
public class Custom2AutoConfig {
@Autowired
Custom2Properties custom2Properties;
@Bean
/**不存在實例才加載,避免重複*/
@ConditionalOnMissingBean(Custom2Server.class)
/**classpath出現Custom2Server才加載*/
@ConditionalOnClass(Custom2Server.class)
/**配置enable true 默認true才加載*/
@ConditionalOnProperty(prefix="custom2", value="enable", matchIfMissing = true)
public Custom2Server custom2Server(){
Custom2Server testServer=new Custom2Server(custom2Properties);
return testServer;
}
}
Custom2Server.java
package com.zyj.config;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:11
*/
public class Custom2Server {
/**
* 初始化
* @param custom2Properties
*/
public Custom2Server(Custom2Properties custom2Properties) {
System.out.println(this.getClass().getName()+"初始化了:打印配置"+ custom2Properties.toString());
}
}
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zyj.config.Custom2AutoConfig
3、測試
step1:pom引入自動配置的包
<dependency>
<groupId>com.zyj</groupId>
<artifactId>springboot-autoconfig-byfile</artifactId>
</dependency>
step2:配置application.properties
server.port=8099
#次自動配置方式通過enable控制開關 不寫設置爲true
custom2.test=測試文件方式自動配置
custom2.enable=true
step3:啓動
step4:取消自動配置
server.port=8099
#次自動配置方式通過enable控制開關 不寫設置爲true
custom2.test=test-auto-by-file
custom2.enable=false
方式二:使用註解@EnableXXX實現開關
1、目錄結構
src
├─main
│ ├─java
│ │ └─com
│ │ └─zyj
│ │ │ SpringbootAutoconfigByAnnoApplication.java ##可忽略
│ │ │
│ │ ├─auto
│ │ │ CustomAutoConfigSelect.java ##自定義配置類過濾 等價spring.factories
│ │ │ EnableCustomAutoConfig.java ##自定義配置註解
│ │ │
│ │ └─config
│ │ CustomAutoConfig.java ##自定配置類
│ │ CustomProperties.java ##配置屬性(可忽略)
│ │ CustomServer.java ##配置服務(可忽略)
│ │
│ └─resources
│ application.properties ##可忽略
│
└─test
└─java
└─com
└─zyj
SpringbootAutoconfigApplicationTests.java ##可忽略
2、關鍵代碼
pom依賴
<!--關鍵依賴 @ConfigurationProperties-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--更加自動配置框架而定-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
EnableCustomAutoConfig.java
package com.zyj.auto;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:26
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**如果使用註解則引入CustomAutoConfigSelect*/
@Import(CustomAutoConfigSelect.class)
public @interface EnableCustomAutoConfig {
}
CustomAutoConfigSelect.java
package com.zyj.auto;
import com.zyj.config.CustomAutoConfig;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:28
*/
public class CustomAutoConfigSelect implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
/** 打印springboot使用的註解annotationMetadata.getAnnotationTypes().forEach(System.out::println);*/
if(annotationMetadata.getAnnotationTypes().contains(EnableCustomAutoConfig.class.getName())){
//配置自動配置需要加載的類
return new String[]{CustomAutoConfig.class.getName()};
}else{
//沒有自動配置則不加載
return new String[0];
}
}
}
CustomProperties.java
package com.zyj.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:35
*/
/**解析spring配置文件中custom開頭的配置信息*/
@ConfigurationProperties(prefix="custom")
public class CustomProperties {
private boolean enable;
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("[");
stringBuilder.append("enable:"+enable);
stringBuilder.append(",");
stringBuilder.append("test:"+test);
stringBuilder.append("]");
return stringBuilder.toString();
}
}
CustomAutoConfig.java
package com.zyj.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:29
*/
/**類似@Import 加載屬性文件(相當於屬性文件@Component)*/
/**與下面@Autowired配合*/
@EnableConfigurationProperties(CustomProperties.class)
public class CustomAutoConfig {
@Autowired
CustomProperties customProperties;
@Bean
/**不存在實例才加載,避免重複*/
@ConditionalOnMissingBean(CustomServer.class)
/**classpath出現CustomServer才加載*/
@ConditionalOnClass(CustomServer.class)
/**配置enable true 默認true才加載*/
@ConditionalOnProperty(prefix="custom", value="enable", matchIfMissing = true)
public CustomServer customServer(){
CustomServer testServer=new CustomServer(customProperties);
return testServer;
}
}
CustomServer.java
package com.zyj.config;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:11
*/
public class CustomServer {
/**
* 初始化
* @param customProperties
*/
public CustomServer(CustomProperties customProperties) {
System.out.println(this.getClass().getName()+"初始化了:打印配置"+ customProperties.toString());
}
}
3、測試
step1:pom引入自動配置的包
<dependency>
<groupId>com.zyj</groupId>
<artifactId>springboot-autoconfig-byanno</artifactId>
</dependency>
step2:springboot加註解 (配置application.properties(可忽略) )
@SpringBootApplication
@EnableCustomAutoConfig/**添加此註解實現自動配置*/
public class SpringbootAutoconfigTestApplication {
//省略
}
server.port=8099
#次自動配置方式通過enable註解即可,enable爲再擴展
custom.test=test-auto-by-anno
custom.enable=true
step3:啓動
step4:關閉自動配置
去除註解即可
不去註解設置false
server.port=8099
#次自動配置方式通過enable註解即可,enable爲再擴展
custom.test=test-auto-by-anno
custom.enable=false