欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2019年12月24日 10:24:27   作者:何其有靜  
這篇文章主要介紹了通過代碼實例了解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;
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • springboot整合kaptcha生成驗證碼功能

    springboot整合kaptcha生成驗證碼功能

    這篇文章主要介紹了springboot整合kaptcha生成驗證碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • Java 進程執(zhí)行外部程序造成阻塞的一種原因

    Java 進程執(zhí)行外部程序造成阻塞的一種原因

    前一陣子在研究文檔展示時使用了java進程直接調用外部程序,其中遇到一個問題花了好長時間才解決,這個問題就是外部程序直接執(zhí)行沒什么問題,但是當使用Java進程執(zhí)行時外部程序就阻塞在那兒不動了。而且這個外部程序在處理某些文件時使用Java進程執(zhí)行是沒問題的
    2014-03-03
  • Java面試題沖刺第二十四天--并發(fā)編程

    Java面試題沖刺第二十四天--并發(fā)編程

    這篇文章主要為大家分享了最有價值的三道關于數(shù)據(jù)庫的面試題,涵蓋內容全面,包括數(shù)據(jù)結構和算法相關的題目、經典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java制作android 日歷代碼分享

    java制作android 日歷代碼分享

    本文給大家分享的是一段使用java制作Android日歷的代碼,非常簡單實用,實現(xiàn)了讀取日歷事件、插入事件、編輯日歷事件、查看日歷等功能,有需要的小伙伴參考下
    2015-03-03
  • StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別

    StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別

    今天小編就為大家分享一篇關于StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 如何利用NetworkInterface獲取服務器MAC地址

    如何利用NetworkInterface獲取服務器MAC地址

    今天介紹一種通用的跨平臺的操作方式,那就是JDK自帶的NetworkInterface接口,該接口在JDK1.4已經出現(xiàn),但是功能比較少,JDK1.6之后新增了不少新功能,比較不錯
    2013-08-08
  • Java歸并排序算法代碼實現(xiàn)

    Java歸并排序算法代碼實現(xiàn)

    歸并(Merge)排序法是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的,下面這篇文章主要給大家介紹了關于Java歸并排序算法的相關資料,需要的朋友可以參考下
    2024-03-03
  • Java中MapStruct入門使用及對比

    Java中MapStruct入門使用及對比

    MapStruct是一個Java注解處理器框架,用于簡化Java Bean之間的映射,本文主要介紹了Java中MapStruct入門使用及對比,感興趣的可以了解一下
    2023-12-12
  • Java實現(xiàn)簡單的銀行管理系統(tǒng)的示例代碼

    Java實現(xiàn)簡單的銀行管理系統(tǒng)的示例代碼

    這篇文章主要介紹了如何利用Java實現(xiàn)簡單的銀行管理系統(tǒng),可以實現(xiàn)存款,取款,查詢等功能,文中的示例代碼講解詳細,感興趣的可以了解一下
    2022-09-09
  • Java的Jackson庫的使用及其樹模型的入門學習教程

    Java的Jackson庫的使用及其樹模型的入門學習教程

    這篇文章主要介紹了Java的Jackson庫的使用及其樹模型入門學習教程,Jackson庫通常被用來作Java對象和JSON的互相轉換,需要的朋友可以參考下
    2016-01-01

最新評論