通過代碼實例了解SpringBoot啟動原理
這篇文章主要介紹了通過代碼實例了解SpringBoot啟動原理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
SpringBoot和Spring相比,有著不少優(yōu)勢,比如自動配置,jar直接運行等等。那么SpringBoot到底是怎么啟動的呢?
下面是SpringBoot啟動的入口:
@SpringBootApplication public class HelloApplication { public static void main(String[] args) { SpringApplication.run(HelloApplication.class, args); } }
一、先看一下@SpringBoot注解:
@Target({ElementType.TYPE}) //定義其使用時機 @Retention(RetentionPolicy.RUNTIME) //編譯程序將Annotation儲存于class檔中,可由VM使用反射機制的代碼所讀取和使用。 @Documented //這個注解應該被 javadoc工具記錄 @Inherited //被注解的類會自動繼承. 更具體地說,如果定義注解時使用了 @Inherited 標記,然后用定義的注解來標注另一個父類, 父類又有一個子類(subclass),則父類的所有屬性將被繼承到它的子類中. @SpringBootConfiguration //@SpringBootConfiguration就相當于@Configuration。JavaConfig配置形式 @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} //自動掃描并加載符合條件的組件。以通過basePackages等屬性來細粒度的定制@ComponentScan自動掃描的范圍,如果不指定,則默認Spring框架實現(xiàn)會從聲明@ComponentScan所在類的package進行掃描。 注:所以SpringBoot的啟動類最好是放在root package下,因為默認不指定basePackages。 ) public @interface SpringBootApplication { @AliasFor( annotation = EnableAutoConfiguration.class ) Class<?>[] exclude() default {}; @AliasFor( annotation = EnableAutoConfiguration.class ) String[] excludeName() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {}; }
所以,實際上SpringBootApplication注解相當于三個注解的組合,@SpringBootConfiguration,@ComponentScan和@EnableAutoConfiguration。
@SpringBootConfiguration和@ComponentScan,很容易知道它的意思,一個是JavaConfig配置,一個是掃描包。關鍵在于@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 {}; }
Springboot應用啟動過程中使用ConfigurationClassParser分析配置類時,如果發(fā)現(xiàn)注解中存在@Import(ImportSelector)的情況,就會創(chuàng)建一個相應的ImportSelector對象, 并調用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata), 這里 EnableAutoConfigurationImportSelector的導入@Import(EnableAutoConfigurationImportSelector.class) 就屬于這種情況,所以ConfigurationClassParser會實例化一個 EnableAutoConfigurationImportSelector 并調用它的 selectImports() 方法。
AutoConfigurationImportSelector implements DeferredImportSelector extends ImportSelector。
下面是AutoConfigurationImportSelector的執(zhí)行過程:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private static final String[] NO_IMPORTS = new String[0]; private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class); private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; public AutoConfigurationImportSelector() { } public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { // 從配置文件中加載 AutoConfigurationMetadata AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = this.getAttributes(annotationMetadata); // 獲取所有候選配置類EnableAutoConfiguration // 使用了內部工具使用SpringFactoriesLoader,查找classpath上所有jar包中的 // META-INF\spring.factories,找出其中key為 // org.springframework.boot.autoconfigure.EnableAutoConfiguration // 的屬性定義的工廠類名稱。 // 雖然參數(shù)有annotationMetadata,attributes,但在 AutoConfigurationImportSelector 的 // 實現(xiàn) getCandidateConfigurations()中,這兩個參數(shù)并未使用 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); //去重 configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); // 應用 exclusion 屬性 this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 應用過濾器AutoConfigurationImportFilter, // 對于 spring boot autoconfigure,定義了一個需要被應用的過濾器 : // org.springframework.boot.autoconfigure.condition.OnClassCondition, // 此過濾器檢查候選配置類上的注解@ConditionalOnClass,如果要求的類在classpath // 中不存在,則這個候選配置類會被排除掉 configurations = this.filter(configurations, autoConfigurationMetadata); // 現(xiàn)在已經找到所有需要被應用的候選配置類 // 廣播事件 AutoConfigurationImportEvent this.fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } } protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { String name = this.getAnnotationClass().getName(); AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); Assert.notNull(attributes, () -> { return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?"; }); return attributes; } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; } public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap(); public SpringFactoriesLoader() { } public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } } private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); // 記錄候選配置類是否需要被排除,skip為true表示需要被排除,全部初始化為false,不需要被排除 boolean[] skip = new boolean[candidates.length]; // 記錄候選配置類中是否有任何一個候選配置類被忽略,初始化為false boolean skipped = false; Iterator var8 = this.getAutoConfigurationImportFilters().iterator(); // 獲取AutoConfigurationImportFilter并逐個應用過濾 while(var8.hasNext()) { AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next(); // 對過濾器注入其需要Aware的信息 this.invokeAwareMethods(filter); // 使用此過濾器檢查候選配置類跟autoConfigurationMetadata的匹配情況 boolean[] match = filter.match(candidates, autoConfigurationMetadata); for(int i = 0; i < match.length; ++i) { if (!match[i]) { // 如果有某個候選配置類不符合當前過濾器,將其標記為需要被排除, // 并且將 skipped設置為true,表示發(fā)現(xiàn)了某個候選配置類需要被排除 skip[i] = true; skipped = true; } } } if (!skipped) { // 如果所有的候選配置類都不需要被排除,則直接返回外部參數(shù)提供的候選配置類集合 return configurations; } else { // 邏輯走到這里因為skipped為true,表明上面的的過濾器應用邏輯中發(fā)現(xiàn)了某些候選配置類 // 需要被排除,這里排除那些需要被排除的候選配置類,將那些不需要被排除的候選配置類組成 // 一個新的集合返回給調用者 List<String> result = new ArrayList(candidates.length); int numberFiltered; for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) { if (!skip[numberFiltered]) { result.add(candidates[numberFiltered]); } } if (logger.isTraceEnabled()) { numberFiltered = configurations.size() - result.size(); logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms"); } return new ArrayList(result); } } /** * 使用內部工具 SpringFactoriesLoader,查找classpath上所有jar包中的 * META-INF\spring.factories,找出其中key為 * org.springframework.boot.autoconfigure.AutoConfigurationImportFilter * 的屬性定義的過濾器類并實例化。 * AutoConfigurationImportFilter過濾器可以被注冊到 spring.factories用于對自動配置類 * 做一些限制,在這些自動配置類的字節(jié)碼被讀取之前做快速排除處理。 * spring boot autoconfigure 缺省注冊了一個 AutoConfigurationImportFilter : * org.springframework.boot.autoconfigure.condition.OnClassCondition **/ protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }
二、下面看一下SpringBoot啟動時run方法執(zhí)行過程
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); this.configureHeadlessProperty(); //從類路徑下META-INF/spring.factories獲取 SpringApplicationRunListeners listeners = getRunListeners(args); //回調所有的獲取SpringApplicationRunListener.starting()方法 listeners.starting(); try { //封裝命令行參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //準備環(huán)境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //創(chuàng)建環(huán)境完成后回調 SpringApplicationRunListener.environmentPrepared();表示環(huán)境準備完成 this.configureIgnoreBeanInfo(environment); //打印Banner圖 Banner printedBanner = printBanner(environment); //創(chuàng)建ApplicationContext,決定創(chuàng)建web的ioc還是普通的ioc context = createApplicationContext(); //異常分析報告 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //準備上下文環(huán)境,將environment保存到ioc中 //applyInitializers():回調之前保存的所有的ApplicationContextInitializer的initialize方法 //listeners.contextPrepared(context) //prepareContext運行完成以后回調所有的SpringApplicationRunListener的contextLoaded() this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新容器,ioc容器初始化(如果是web應用還會創(chuàng)建嵌入式的Tomcat) //掃描,創(chuàng)建,加載所有組件的地方,(配置類,組件,自動配置) this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //所有的SpringApplicationRunListener回調started方法 listeners.started(context); //從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進行回調, //ApplicationRunner先回調,CommandLineRunner再回調 this.callRunners(context, applicationArguments); } catch (Throwable ex) { this.handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //所有的SpringApplicationRunListener回調running方法 listeners.running(context); } catch (Throwable ex) { this.handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //整個SpringBoot應用啟動完成以后返回啟動的ioc容器 return context; }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別
今天小編就為大家分享一篇關于StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12如何利用NetworkInterface獲取服務器MAC地址
今天介紹一種通用的跨平臺的操作方式,那就是JDK自帶的NetworkInterface接口,該接口在JDK1.4已經出現(xiàn),但是功能比較少,JDK1.6之后新增了不少新功能,比較不錯2013-08-08Java實現(xiàn)簡單的銀行管理系統(tǒng)的示例代碼
這篇文章主要介紹了如何利用Java實現(xiàn)簡單的銀行管理系統(tǒng),可以實現(xiàn)存款,取款,查詢等功能,文中的示例代碼講解詳細,感興趣的可以了解一下2022-09-09