温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么通过代码实例了解SpringBoot启动原理

发布时间:2021-10-15 17:50:17 来源:亿速云 阅读:101 作者:柒染 栏目:编程语言

本篇文章给大家分享的是有关怎么通过代码实例了解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