@SpringbootApplication
這個註解標記的類為Springboot程式的主配置類,進入這個註解
@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 {}
可以發現有兩個註解比較關鍵**@SpringBootConfiguration** @EnableAutoConfiguration
根據字面意思為 Springboot配置 和 自動配置
@SpringBootConfiguration進入這個註解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {}
發現有@Configuration註解 該註解在Spring中也有接觸就是表示一個配置類的意思 javaConfig來代替applicationContext.xml這種配置檔案的
再進入@Configuration
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Configuration {}
發現有@Conponent註解 這是註解會被Spring自動裝載成bean元件
所以可以知道@SpringbootApplication這個註解標記的類是一個Spring的配置類,且會被註冊成bean@EnableAutoConfiguration使能夠自動裝配
進入該註解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {}
有一個@AutoConfigurationPackage 和匯入了一個類@Import(AutoConfigurationImportSelector.class)
自動配置包註解 自動配置匯入選擇器類@AutoConfigurationPackage進入該註解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(AutoConfigurationPackages.Registrar.class)public @interface AutoConfigurationPackage {}
匯入了一個AutoConfigurationPackages.Registrar.class
自動配置包類的靜態內部類 註冊器類 打個的斷點debug
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { //將註解元資料進行註冊 getPackageName @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
可以發現已經能夠獲取到包名說白了就是將主配置類(即@SpringBootApplication標註的類)的所在包及子包裡面所有元件掃描載入到Spring容器。所以包名一定要注意。@Import(AutoConfigurationImportSelector.class)進入該類
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
可發現配置都透過這個方法獲得
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> 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; }protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } protected ClassLoader getBeanClassLoader() { return this.beanClassLoader; }//進入SpringFactoriesLoader.loadFactoryNames()方法public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }//進入 loadSpringFactories(classLoader)private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? //public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
透過ClassLoader載入使用了這個**@EnableAutoConfiguration**註解的類
這個方法會載入jar包中 META-INF/spring.factories 檔案中配置的配置物件
SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動配置類匯入到容器中,自動配置類就生效,幫我們進行自動配置工作。以前我們需要自己配置的東西,自動配置類都幫我們完成了。
spring.factories檔案也是一組一組的key=value的形式,其中一個key是EnableAutoConfiguration類的全類名,而它的value是一個xxxxAutoConfiguration的類名的列表,這些類名以逗號分隔,如下圖所示:
接下來看看都匯入了哪些元件被新增到容器中
selectImports方法
@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()); }//進入 getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata) 打個斷點看看;
可以看見每個要匯入的元件都以全類名的方法返回
@Conditional xxx條件註解,透過判斷類路徑下有沒有相應配置的 jar 包來確定是否載入和自動配置這個類。
具體幾個@Conditon*註解的含義
@ConditionalOnBean : 僅僅在當前上下文中存在某個物件時,才會例項化一個Bean
@ConditionalOnMissingBean : DI容器中不存在該型別Bean時起效
@ConditionalOnClass : 某個class位於類路徑上,才會例項化一個Bean),該註解的引數對應的類必須存在,否則不解析該註解修飾的配置類
@ConditionalOnMissingClass : classpath中不存在該類時起效
@ConditionalOnExpression :當表示式為true的時候,才會例項化一個Bean
@ConditionalOnMissingBean :僅僅在當前上下文中不存在某個物件時,才會例項化一個Bean,該註解表示,如果存在它修飾的類的bean,則不需要再建立這個bean,可以給該註解傳入引數例如
@ConditionOnMissingBean(name = "example"),這個表示如果name為“example”的bean存在,這該註解修飾的程式碼塊不執行
@ConditionalOnMissingClass :某個class類路徑上不存在的時候,才會例項化一個Bean
@ConditionalOnSingleCandidate : DI容器中該型別Bean只有一個或@Primary的只有一個時起效
@ConditionalOnProperty : 引數設定或者值一致時起效
@ConditionalOnResource : 指定的檔案存在時起效
@ConditionalOnJndi : 指定的JNDI存在時起效
@ConditionalOnJava : 指定的Java版本存在時起效
@ConditionalOnWebApplication : Web應用環境下起效
@ConditionalOnNotWebApplication : 非Web應用環境下起效
結論SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值將這些值作為自動配置類匯入容器 , 自動配置類就生效 , 幫我們進行自動配置工作;整個J2EE的整體解決方案和自動配置都在springboot-autoconfigure的jar包中;它會給容器中匯入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中匯入這個場景需要的所有元件 , 並配置好這些元件 ;有了自動配置類 , 免去了我們手動編寫配置注入功能元件等的工作;SpringApplication.run分析分析該方法主要分兩部分,一部分是SpringApplication的例項化,二是run方法的執行;
SpringApplication這個類主要做了以下四件事情:1、推斷應用的型別是普通的專案還是Web專案
2、查詢並載入所有可用初始化器 , 設定到initializers屬性中
3、找出所有的應用程式監聽器,設定到listeners屬性中
4、推斷並設定main方法的定義類,找到執行的主類
檢視構造器:
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { // ...... this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass();}
run 方法流程分析