SpringBoot——自定義自動配置與起步依賴
SpringBoot為我們提供了靈活強大的自動配置與起步依賴功能,接下來我們參考其實現原理,實現專屬於我們自己的自動配置與起步依賴。
不僅如此,我們對其稍作修改,讓它適用於非SpringBoot環境,甚至是低版本的Spring Framework環境
1自動配置
在編寫自己的自動配置之前,我們先來看一下SpringBoot自動配置類的實現原理。
SpringBoot可以根據CLASSPATH、配置項等條件自動進行常規配置,省去了我們自己手動把一模一樣的配置複製來複制去的麻煩。這樣大大提升了開發效率!
在這之前,我們已經看到過@SpringBootApplication註解了,檢視這個註解,可以發現它上面新增了一個@EnableAutoConfiguration,基於這個註解就可以開啟自動配置功能。
這兩個註解上都有exclude屬性,我們可以在其中排除一些不想啟用的自動配置類。
如果不想啟用自動配置功能,也可以在配置檔案中配置spring.boot.enableautoconfiguration=false,關閉該功能。
1.1自動配置的實現原理
自動配置類其實就是新增了@Configuration的普通Java配置類,它利用Spring Framework 4.0加入的條件註解@Conditional來實現“根據特定條件啟用相關配置類”,註解中傳入的Condition類就是不同條件的判斷邏輯。SpringBoot內建了很多條件註解,如下表所示:
條件註解 | 生效條件 |
---|---|
@ConditionalOnBean | 存在特定名稱、特定型別、特定泛型引數或帶有特定註解的Bean |
@ConditionalOnMissingBean | 與前者相反,不存在特定的Bean |
@ConditionalOnClass | 存在特定的類 |
@ConditionalOnMissingClass | 與前者相反,不存在特定類 |
@ConditionalOnCloudPlatform | 執行在特定的雲平臺上,截止2.6.3版本,代表雲平臺的列舉類支援無雲平臺,可以透過spring.main.cloud-platform配置強制使用的雲平臺 |
@ConditionalOnExpression | 指定的SpEL表示式為真 |
@ConditionalOnJava | 執行在滿足條件的Java上,可以比指定版本新,也可以比指定版本舊 |
@ConditionalOnJndi | 指定的JNDI位置必須存在一個,如沒有指定,則需要存在InitalContext |
@ConditionalOnProperty | 屬性值滿足特定條件,比如給定的屬性值都不能為false |
@ConditionalOnResource | 存在特定資源 |
@ConditionalOnSingleCandidate | 當前上下文中,特定型別的Bean有且僅有一個 |
@ConditionalOnWarDeployment | 應用程式是透過傳統的War方式部署的,而非內嵌容器 |
@ConditionalOnWebApplication | 應用程式是一個Web應用程式 |
@ConditionalOnNotWebApplication | 與前者相反,應用程式不是一個Web應用程式 |
?更多如圖所示,就不一一列舉了:
以@ConditionalOnClass註解為例,它的定義如下所示,@Target指明該註解可用於型別和方法定義,@Retention指明註解的資訊在執行時也能獲取到,而其中最關鍵的就是OnClassCondition條件類,裡面是具體的條件計算邏輯:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
/**
* The classes that must be present. Since this annotation is parsed by loading class
* bytecode, it is safe to specify classes here that may ultimately not be on the
* classpath, only if this annotation is directly on the affected component and
* <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
* use this annotation as a meta-annotation, only use the {@link #name} attribute.
* @return the classes that must be present
*/
Class<?>[] value() default {};
/**
* The classes names that must be present.
* @return the class names that must be present.
*/
String[] name() default {};
}
瞭解了條件註解後,再來看看它們是如何與配置類結合使用的。以JdbcTemplateAutoConfiguration為例:
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class,
NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {
}
可以看到這個配置類的生效條件是存在DataSource和JdbcTemplate類,且在上下文中只能有一個DataSource。此外,這個自動配置需要在DataSourceAutoConfiguration之後再配置(可以用@AutoConfigureBefore、@AutoConfigureAfter和@AutoConfigureOrder來控制自動配置的順序)
2編寫自已的自動配置
根據上面的描述,我們很容易想到,要編寫自己的自動配置,只需要以下三個步驟:
(1)編寫常規的配置類
(2)為配置類增加生效條件與順序
(3)在/META-INF/spring.factories檔案中新增自動配置類
2.1建立專案
在Spring Initializr中,建立一個Maven工程,新增如下依賴:
<dependencies>
<!-- 新增自動配置所使用的包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 使用者在寫配置檔案時,會有提示效果 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.2編寫一個簡單的MyAutoConfigure類和配置類Bean
編寫一個簡單的MyAutoConfigure類,上面增加了@Configuration註解,表示這是一個配置類,這個配置類的生效條件是myconfig.ready屬性的值為true,除此之外的值或者不存在該屬性時MyAutoConfigure都不會生效。
/**
* TODO 1、編寫配置類並指定檔案
*
* @author ss_419
* @version 1.0
* @date 2023/3/17 21:24
*/
@Configuration
@EnableConfigurationProperties(CommonBean.class)
@ConditionalOnProperty(name = "myconfig.ready",havingValue = "true")
public class MyAutoConfigure {
}
/**
* TODO 2、建立配置類Bean
*
* @author ss_419
* @version 1.0
* @date 2023/3/17 22:40
*/
@ConfigurationProperties("myconfigbean")
@Data
public class MyConfigBean {
private boolean ready;
private String info;
}
2.3配置spring.factories檔案
為了讓SpringBoot能找到我們寫的這個配置類,我們需要在src/resources目錄中建立META-INF/spring.factories檔案,其內容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.pp.myspringbootstart.beans.MyAutoConfigure
2.4測試自動配置是否生效
@SpringBootTest
public class MyConfigurationEnableTest {
@Autowired
private ApplicationContext applicationContext;
@Test
void testPropertiesBeanAvailableTest() throws Exception {
assertNotNull(applicationContext.getBean(MyConfigBean.class));
assertTrue(applicationContext.containsBean("org.pp.myspringbootstart.beans.MyConfigBean"));
}
@Test
void testPropertyValues(){
MyConfigBean bean = applicationContext.getBean(MyConfigBean.class);
assertTrue(bean.isReady());
assertEquals("hello", bean.getInfo());
}
}