中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么通過代碼實例了解SpringBoot啟動原理

發布時間:2021-10-15 17:50:17 來源:億速云 閱讀:120 作者:柒染 欄目:編程語言

本篇文章給大家分享的是有關怎么通過代碼實例了解SpringBoot啟動原理,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

SpringBoot和Spring相比,有著不少優勢,比如自動配置,jar直接運行等等。那么SpringBoot到底是怎么啟動的呢?

下面是SpringBoot啟動的入口:

@SpringBootApplicationpublic 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框架實現會從聲明@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分析配置類時,如果發現注解中存在@Import(ImportSelector)的情況,就會創建一個相應的ImportSelector對象, 并調用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata), 這里 EnableAutoConfigurationImportSelector的導入@Import(EnableAutoConfigurationImportSelector.class) 就屬于這種情況,所以ConfigurationClassParser會實例化一個 EnableAutoConfigurationImportSelector 并調用它的 selectImports() 方法。

AutoConfigurationImportSelector implements DeferredImportSelector extends ImportSelector。

下面是AutoConfigurationImportSelector的執行過程:

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       // 的屬性定義的工廠類名稱。      // 雖然參數有annotationMetadata,attributes,但在 AutoConfigurationImportSelector 的      // 實現 getCandidateConfigurations()中,這兩個參數并未使用      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);     // 現在已經找到所有需要被應用的候選配置類     // 廣播事件 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,表示發現了某個候選配置類需要被排除          skip[i] = true;          skipped = true;        }      }    }    if (!skipped) {    // 如果所有的候選配置類都不需要被排除,則直接返回外部參數提供的候選配置類集合      return configurations;    } else {    // 邏輯走到這里因為skipped為true,表明上面的的過濾器應用邏輯中發現了某些候選配置類    // 需要被排除,這里排除那些需要被排除的候選配置類,將那些不需要被排除的候選配置類組成    // 一個新的集合返回給調用者      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用于對自動配置類   * 做一些限制,在這些自動配置類的字節碼被讀取之前做快速排除處理。   * spring boot autoconfigure 缺省注冊了一個 AutoConfigurationImportFilter :   * org.springframework.boot.autoconfigure.condition.OnClassCondition  **/  protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);  }

二、下面看一下SpringBoot啟動時run方法執行過程

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 {    //封裝命令行參數    ApplicationArguments applicationArguments = new DefaultApplicationArguments(          args);    //準備環境     ConfigurableEnvironment environment = prepareEnvironment(listeners,          applicationArguments);    //創建環境完成后回調     SpringApplicationRunListener.environmentPrepared();表示環境準備完成    this.configureIgnoreBeanInfo(environment);    //打印Banner圖    Banner printedBanner = printBanner(environment);    //創建ApplicationContext,決定創建web的ioc還是普通的ioc     context = createApplicationContext();    //異常分析報告    exceptionReporters = getSpringFactoriesInstances(          SpringBootExceptionReporter.class,          new Class[] { ConfigurableApplicationContext.class }, context);    //準備上下文環境,將environment保存到ioc中    //applyInitializers():回調之前保存的所有的ApplicationContextInitializer的initialize方法     //listeners.contextPrepared(context)     //prepareContext運行完成以后回調所有的SpringApplicationRunListener的contextLoaded()    this.prepareContext(context, environment, listeners, applicationArguments,          printedBanner);    //刷新容器,ioc容器初始化(如果是web應用還會創建嵌入式的Tomcat)    //掃描,創建,加載所有組件的地方,(配置類,組件,自動配置)    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;}

以上就是怎么通過代碼實例了解SpringBoot啟動原理,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

九龙城区| 金坛市| 永登县| 黄平县| 临泉县| 盐山县| 武城县| 杂多县| 闵行区| 横山县| 阜平县| 安义县| 衡山县| 石河子市| 两当县| 台南市| 杭锦旗| 启东市| 京山县| 宜丰县| 偃师市| 崇仁县| 龙门县| 濮阳市| 余江县| 桃园县| 九寨沟县| 萨迦县| 襄樊市| 天津市| 黄大仙区| 榆林市| 阜宁县| 山阴县| 田东县| 涟水县| 旬邑县| 泰来县| 信阳市| 普宁市| 曲周县|