關於@SpringBootApplication與@SpringBootTest的區別及用法
@SpringBootApplication與@SpringBootTest區別用法
1 @SpringBootApplication 註解的應用
一般情況我們使用 @SpringBootApplication 註解來啟動 SpringBoot 項目
它其實隻相當於 @Configuration、@EnableAutoConfiguration、@ComponentScan(包含瞭兩個filter)
@SpringBootApplication public class FrameworkUnitRealTestApp { public static void main(String[] args) { SpringApplication.run(FrameworkUnitRealTestApp.class, args); } }
2 @SpringBootTest 註解的應用
一般情況我們使用 @SpringBootTest 和 @RunWith(SpringRunner.class) 註解來啟動 SpringBoot 測試項目
@RunWith(SpringRunner.class) @SpringBootTest public class FrameworkUnitRealTestApp { @Test public void test() {} }
3 @SpringBootApplication 和 @SpringBootTest 的區別
這兩個註解的區別的核心在於兩個註解:@EnableAutoConfiguration、@ComponentScan(包含瞭兩個filter)
@EnableAutoConfiguration 啟動瞭所有的自動配置類
@ComponentScan(包含瞭兩個filter):在掃描階段過濾掉 @TestComponent 等專屬於測試的類和過濾掉被 @Configuration 註解的自動配置類(使得自動配置類不會在掃描階段就被註冊 beanDefinition,因為 自動配置類的優先級應該是最低的)
可以看出 @SpringBootTest 並沒有啟用任何自動配置類,所以就不需要加 AutoConfigurationExcludeFilter 瞭
springboot 通過引入 @Test** 註解來在 測試環境下 引入不同的自動配置類!
4 @ComponentScan(包含瞭兩個filter) 解析
詳細的代碼如下:添加瞭 TypeExcludeFilter 和 AutoConfigurationExcludeFilter 兩個 excludeFilter
作用:掃描包的時候過濾掉被這兩個 Filter 匹配的類!
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
4.1 TypeExcludeFilter 解析
主要移除測試相關的類
public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) { Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory) .getBeansOfType(TypeExcludeFilter.class).values(); for (TypeExcludeFilter delegate : delegates) { if (delegate.match(metadataReader, metadataReaderFactory)) { return true; } } } return false; } } //delegate.match 走這個類的 match 方法 class TestTypeExcludeFilter extends TypeExcludeFilter { private static final String[] CLASS_ANNOTATIONS = { "org.junit.runner.RunWith", "org.junit.jupiter.api.extension.ExtendWith" }; private static final String[] METHOD_ANNOTATIONS = { "org.junit.Test", "org.junit.platform.commons.annotation.Testable" }; @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //是否被 @TestComponent 及其父註解註釋 if (isTestConfiguration(metadataReader)) {return true;} //類上或類中方法上有沒有 CLASS_ANNOTATIONS、METHOD_ANNOTATIONS 中的註解 if (isTestClass(metadataReader)) {return true;} String enclosing = metadataReader.getClassMetadata().getEnclosingClassName(); if (enclosing != null) { //遞歸內部類、父類 if (match(metadataReaderFactory.getMetadataReader(enclosing), metadataReaderFactory)) { return true; } } return false; } }
4.2 AutoConfigurationExcludeFilter 解析
主要移除被 @Configuration 修飾的 自動配置類
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //如果被 @Configuration 註解,並且是 自動配置類就返回 true,即匹配成功 //註:被 @Component 等註解並不匹配 return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader); } }
5 @EnableAutoConfiguration 註解解析
作用:啟用自動配置類
@AutoConfigurationPackage //啟用 AutoConfigurationImportSelector 配置類:掃描得到所有自動配置類 @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; //定義不啟用的 自動配置類 Class<?>[] exclude() default {}; //同上 String[] excludeName() default {}; } //這個註解主要是向容器中註冊 AutoConfigurationPackages.Registrar 類用來存儲自動配置包 @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {} //關鍵:這個類繼承瞭 DeferredImportSelector 接口,所以是到最後才解析的!! public class AutoConfigurationImportSelector implements DeferredImportSelector{ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
6 @…Test 註解
Spring Boot 中文文檔 對每個 @…Test 註解導入的自動配置類做瞭詳細的說明
SpringBootTest對比SpringBootApplication
SpringBootTest 是測試使用類的註解,標志這個類是測試用例。
具體看下源碼分析
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @BootstrapWith(SpringBootTestContextBootstrapper.class) @ExtendWith({SpringExtension.class}) public @interface SpringBootTest {
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication {
對比顯示都是復合註解,並且前四個註解是一樣的,後面區分BootstrapWith和ExtendWith這兩個是測試中包含的
BootstrapWith這個註解中有一個參數為SpringBootTestContextBootstrapper
具體可以看下裡面是什麼
這裡面申明瞭一些程序運行所在包的路徑,在去查看繼承的頂級類可以追溯到TestContextBootstrapper 這個接口 :
從裡面的方法可以看到是在運行的時候設置上下文 以及如何獲取上下文,來提供測試啟動的必須值。
接下來看下 ExtendWith 這個註解類
這個主要看裡面的SpringExtension這個參數
可以看出這個實現瞭很多接口,來處理測試需要的各種通知處理,以及在測試接口時可以提前處理請求參數。
SpringBootApplication中的復合註解則是掃描一些包和配置。雖然測試也是項目啟動的一種,可以看到裡面實現還是有些區別的。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Springboot項目實現將類從@ComponentScan中排除
- SpringBoot自動配置特點與原理詳細分析
- 詳解Spring系列之@ComponentScan自動掃描組件
- Springboot @Configuration與自動配置詳解
- SpringBoot自動配置原理分析