「SpringBoot2.x」-自定義Spring boot Starter

「SpringBoot2.x」-自定義Spring boot Starter

SpringBoot的方便快捷主要體現之一starter pom,Spring Boot為我們提供了簡化的企業級開發原型場景的starter pom,只要 使用了應用場景所需要的starter pom,只需要starter約會對應的即可,即可以得到Spring Boot為我們提供的自動配置的Bean。

然而,可能在很多情況下,我們需要自定義stater,這樣可以方便公司內部系統調用共同的配置模塊的時候可以自動進行裝載配置。這樣,很多公司將生產數據庫的密碼託管在公司的另外一個專門管理生產密碼的系統上,公司每個系統需要使用的時候都需要調用其方法進行使用,現在可以通過starter自動配置的形式進行配置。

1. SpringBoot Starter源碼分析

問:@SpringBootApplication註解中核心註解@EnableAutoConfiguration註解在啟動器起什麼作用呢?

@EnableAutoConfiguration原始碼分析:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class>[] exclude() default {};
	String[] excludeName() default {};
}
複製代碼

可以從二進制拋光關鍵功能是@import註解引入自動配置功能類的
AutoConfigurationImportSelector類,主要方法
getCandidateConfigurations()使用了


SpringFactoriesLoader.loadFactoryNames()方法加載META-INF / spring.factories的文件(spring.factories聲明具體自動配置)。

protected List getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
}
複製代碼

問:通常情況下,starter會根據條件進行操作處理,根據不同條件創建不同Bean。在SpringBoot有一些註釋解可用呢?

可使用
org.springframwork.boot.autoconfigure.condition的條件註釋解,具體如下所示:

註解 解析 @ConditionalOnBean 當容器裡有指定的Bean的條件下。 @ConditionalOnClass 當類路徑下有指定的類的條件下。 @ConditionalOnExpression 基於SpEL表達作為判斷條件。 @ConditionalOnJava 根據JVM版本作為判斷條件。 @ConditionalOnJndi 在JNDI存在的條件下查找指定的位置。 @ConditionalOnMissingBean 當容器裡沒有指定Bean的情況下。 @ConditionalOnMissingClass 當類路徑下沒有指定的類的條件下。 @
ConditionalOnNotWebApplication 當前項目不是Web項目的條件下。 @ConditionalOnProperty 指定的屬性是否有指定的值。 @ConditionalOnResource 類路徑是否有指定的值。 @
ConditionalOnSingleCandidate 當指定豆在容器中只有一個,或者雖然有多個但是指定首選的豆。 @
ConditionalOnWebApplicatio 當前項目是Web項目的條件下。 2.自定啟動器

在此將模擬公司獲取生產密碼模塊進行自定義starter demo

2.1核心依賴

 
 
  
 org.springframework.boot
 spring-boot-dependencies
 2.1.5.RELEASE
 pom
 import
 
 


 
 org.springframework.boot
 spring-boot-autoconfigure
 

複製代碼
 

2.2服務類服務以及屬性配置注入

PasswordService服務類:

public class PasswordService {
 //第三方系統獲取密碼所需的key
 private String objectKey;
 @Autowired
 //模擬的第三方系統service
 private ThirdPartySystemService thirdPartySystemService;
 public String getSystemPassword(String objectKey,String originalPassord){
 if(StringUtils.isEmpty(objectKey)){
 return originalPassord;
 }
 //從第三方系統獲取密碼
 String password= thirdPartySystemService.getPassword(objectKey);
 //返回密碼
 return password!=null?password:originalPassord;
 }
}
//模擬第三方系統service
public class ThirdPartySystemService {
 public String getPassword(String objectKey){
 //返回一個32位隨機數
 return UUID.randomUUID().toString();
 }
}
複製代碼

屬性配置類:

//通過@ConfigurationProperties註解獲取屬性值
@ConfigurationProperties(prefix = "project.starter")
public class BaseServiceProperties {
 private String serviceName;
 private String serviceVersion;
 public String getServiceName() {
 return serviceName;
 }
 public void setServiceName(String serviceName) {
 this.serviceName = serviceName;
 }
 public String getServiceVersion() {
 return serviceVersion;
 }
 public void setServiceVersion(String serviceVersion) {
 this.serviceVersion = serviceVersion;
 }
}
複製代碼

配置屬性使用類:

public class BaseStarterService {
 public void addServiceName(BaseServiceProperties baseServiceProperties){
 System.out.println("serviceName:"+baseServiceProperties.getServiceName()+"----"+"serviceVersion"+baseServiceProperties.getServiceVersion());
 }
}
複製代碼

其他類:

//判斷是否windows系統
public class WindowsCondition implements Condition {
 private final static String WINDOWS="Windows";
 /**
 * ConditionContext:判斷條件能使用的上下文(環境)
 * AnnotatedTypeMetadata:註釋信息
 */
 public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
 //獲取當前環境變量
 Environment environment=conditionContext.getEnvironment();
 //獲取bean註冊器
 BeanDefinitionRegistry registry = conditionContext.getRegistry();
 //能獲取到ioc使用的beanfactory
 ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
 //獲取環境變量中操作系統
 String property = environment.getProperty("os.name");
 //判斷操作系統是否為windows
 if(property.contains(WINDOWS)){
 //判斷是否存在baseWindowsSevice類,不存在則進行bean註冊
 boolean isWindowsSevice = registry.containsBeanDefinition("baseStarterService");
 if(!isWindowsSevice){
 //指定Bean定義信息;(Bean的類型,Bean的一系列信息)
 RootBeanDefinition beanDefinition = new RootBeanDefinition(BaseStarterService.class);
 //註冊一個Bean,指定bean名
 registry.registerBeanDefinition("baseStarterService", beanDefinition);
 BaseStarterService windowsSevice = (BaseStarterService)beanFactory.getBean("baseStarterService");
 }
 return true;
 }
 return false;
 }
}
複製代碼

2.3自動配置類

代碼解讀:

  • @EnableConfigurationProperties:讀取配置文件的屬性
  • @Import:引入其他配置類或自定義類
  • @Conditional:判斷當前環境是否為windows,是則註冊該類
  • @ConditionalOnProperty:判斷屬性spring.project.ThirdPartySystemService.isPassword是否等於true,不為true則不註冊該類
  • @ConditionalOnClass:判斷IOC容器中是否存在ThirdPartySystemService類,存在則創建PasswordService bean
@Configuration
//自動加載配置文件屬性值
@EnableConfigurationProperties(BaseServiceProperties.class)
@Import(BeanConfiguration.class)
//判斷當前環境是否為windows
@Conditional(WindowsCondition.class):
//判斷屬性spring.project.ThirdPartySystemService.isPassword是否等於true
@ConditionalOnProperty(prefix = "spring.project.ThirdPartySystemService",value = "enablePassword", havingValue = "true",matchIfMissing = true)
public class AutoConfigurationPassoword {
 @Autowired
 private BaseServiceProperties baseServiceProperties;
 @Autowired
 private BaseStarterService baseWindowsService;
 //加載第三方系統service
 @Bean("thirdPartySystemService")
 public ThirdPartySystemService thirdPartySystemService(){
 baseWindowsService.addServiceName(baseServiceProperties);
 return new ThirdPartySystemService();
 }
 @Bean
 //判斷IOC容器中是否存在ThirdPartySystemService類,存在則創建PasswordService bean
 @ConditionalOnClass(ThirdPartySystemService.class)
 public PasswordService passwordService(){
 baseWindowsService.addServiceName(baseServiceProperties);
 return new PasswordService();
 }
}
複製代碼

2.4註冊配置

想自動配置生效,需要註冊自動配置類,在即src/main/resources下新建METAINF/spring.factories在。spring.factorie配置如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 com.cn.ccww.configuration.AutoConfigurationPassoword
複製代碼

如果有多個自動配置,則用“”“替換,此處“ \”是為了換行後還能夠讀取到屬性。

3.測試自定義啟動器

3.1進口依賴

 
 
 spring-boot-starter-base-service
 com.cn.ccww
 1.0-SNAPSHOT
 

複製代碼

3.2 application.properties屬性

application.properties文件有對應的對齊是否可以啟動自定義starter,還可以設置starter所需的屬性。如下所示:

//自定義Starter配置
//當該屬性的值不為true時,才不會啟動自定義starter
spring.project.ThirdPartySystemService.enablePassword=true
project.starter.serviceName=ccww
project.starter.serviceVersion=1.0
複製代碼

4.總結

由上所述,starter的大體的工作流程:

  • SpringBoot啟動時會自動搜索包含spring.factories文件的JAR包;
  • 根據spring.factories文件加載自動配置類AutoConfiguration;
  • 通過AutoConfiguration類,加載滿足條件(@ConditionalOnXxx)的bean到Spring IOC容器中;
  • 使用者可以直接使用自動加載到IOC的bean。


分享到:


相關文章: