SpringBoot启动流程及自动配置

SpringBoot启动流程源码:

1、启动SpringBoot启动类SpringbootdemoApplication中的main方法。

@SpringBootApplication
public class SpringbootdemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootdemoApplication.class, args);
    }
}

2、调用SpringApplication.run(SpringbootdemoApplication.class, args),该方法是一个静态方法。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   return run(new Class<?>[] { primarySource }, args);
}

3、继续调用SpringApplication内部的run方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

,且构建了一个SpringApplication对象,应用程序将从指定的主要来源加载Bean

public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   //resourceLoader赋值为Null
   this.resourceLoader = resourceLoader;
   //primarySources不为空,继续向下执行。为空抛异常
   Assert.notNull(primarySources, "PrimarySources must not be null");
   //将SpringbootdemoApplication(启动类)赋值给primarySources 
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   //从classpath类路径推断Web应用类型,有三种Web应用类型,分别是
   //NONE: 该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器
   //SERVLET: 该应用程序应作为基于 servlet 的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器。
   //REACTIVE: 该应用程序应作为响应式 Web 应用程序运行,并应启动嵌入式响应式 Web 服务器
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //初始化bootstrapRegistryInitializers,通过getSpringFactoriesInstances()获取工厂实例,
   //底层使用的是反射Class<?> instanceClass = ClassUtils.forName(name, classLoader)动态加载实例对象。
   this.bootstrapRegistryInitializers = new ArrayList<>(
         getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
   //初始化ApplicationContextInitializer集合
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //初始化ApplicationListener
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //获取StackTraceElement数组遍历,通过反射获取堆栈中有main方法的类。
   this.mainApplicationClass = deduceMainApplicationClass();
}

4、运行 Spring 应用程序,创建并刷新一个新的 ApplicationContext。

public ConfigurableApplicationContext run(String... args) {
   long startTime = System.nanoTime();
   //通过BootstrapRegistryInitializer来initialize默认的DefaultBootstrapContext
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   //配置java.awt.headless属性
   configureHeadlessProperty();
   //获取SpringApplicationRunListeners监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //启动SpringApplicationRunListeners监听,表示SpringApplication启动(触发ApplicationStartingEvent事件)
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      //创建ApplicationArguments对象,封装了args参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      //做相关环境准备,绑定到SpringApplication,返回可配置环境对象ConfigurableEnvironment 
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      //配置spring.beaninfo.ignore,设置为true.即跳过搜索Bean信息
      configureIgnoreBeanInfo(environment);
      //控制台打印SpringBoot的Banner(横幅)标志
      Banner printedBanner = printBanner(environment);
      //根据WebApplicationType从ApplicationContextFactory工厂创建ConfigurableApplicationContext
      context = createApplicationContext();
      //设置ConfigurableApplicationContext中的ApplicationStartup为DefaultApplicationStartup
      context.setApplicationStartup(this.applicationStartup);
      //应用所有的ApplicationContextInitializer容器初始化器初始化context,触发ApplicationContextInitializedEvent事件监听,打印启动日志信息,启动Profile日志信息。
      //ConfigurableListableBeanFactory中注册单例Bean(springApplicationArguments),并为该BeanFactory中的部分属性赋值。
      //加载所有的source.并将Bean加载到ConfigurableApplicationContext,触发ApplicationPreparedEvent事件监听
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      //刷新容器(在方法中集成了Web容器具体请看 https://editor.csdn.net/md/?articleId=123136262)
      refreshContext(context);
      //刷新容器的后置处理(空方法)
      afterRefresh(context, applicationArguments);
      //启动花费的时间
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         //打印日志Started xxx in xxx seconds (JVM running for xxxx)
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      //触发ApplicationStartedEvent事件监听。上下文已刷新,应用程序已启动。
      listeners.started(context, timeTakenToStartup);
      //调用ApplicationRunner和CommandLineRunner
      callRunners(context, applicationArguments);
   }
   //处理运行时发生的异常,触发ApplicationFailedEvent事件监听
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      //启动准备消耗的时间
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      //在run方法完成前立即触发ApplicationReadyEvent事件监听,表示应用上下文已刷新,并且CommandLineRunners和ApplicationRunners已被调用。
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

SpringBoot的自动配置:

SpringBoot的启动类上总是有@SpringBootApplication这个注解。接下来我们来了解一下这个注解。进入@SpringBootApplication源码:

@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 {
   ......
}

由此可见,@SpringBootApplication注解是一个组合注解,由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan注解组成。

@SpringBootConfiguration其实就是一个@Configuration,表明这是一个配置类,可以向容器注入组件。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
 	......
}

@EnableAutoConfiguration由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})注解组成

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
   ......
}

@AutoConfigurationPackage内部用到了@Import导入Registrar

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
  ......
}

Registrar实现了ImportBeanDefinitionRegistrar接口,因此可将组件都扫描注冊到 spring 容器中

static class Registrar implements ImportBeanDefinitionRegistrar,

DeterminableImports {

Registrar() {

}

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

AutoConfigurationPackages.register(registry, (String[])(new

AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new

String[0]));

}

public Set determineImports(AnnotationMetadata metadata) {

return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));

}

}

@AutoConfigurationPackage将主配置类(@SpringBootApplication
标注的类)所在包下的所有组件都扫描注册到Spring容器中。

@Import({AutoConfigurationImportSelector.class}) 将AutoConfigurationImportSelector(自动配置导入选择器)导入容器中

AutoConfigurationImportSelector类中的selectImports()方法的作用是选择导入过滤后的自动配置

 public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

getAutoConfigurationEntry(AnnotationMetadata

annotationMetadata)根据annotationMetadata(即我们的启动类SpringbootdemoApplication)获取AutoConfigurationEntry

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            //获取注解属性
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //从META-INF/spring.factories中获取候选配置。
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            //去除重复配置
            configurations = this.removeDuplicates(configurations);
            //获取注解中的排除项
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            //检查排除类
            this.checkExcludedClasses(configurations, exclusions);
            //从上面的候选配置中移除所有排除的配置类
            configurations.removeAll(exclusions);
            //通过ConfigurationClassFilter筛选配置
            configurations = this.getConfigurationClassFilter().filter(configurations);
            //触发自动配置导入事件
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            //返回排除后的自动配置Entry
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

总结SpringBoot启动原理

1、调用有@SpringBootApplication注解的启动类的main方法

2、通过调用SpringApplication内部的run()方法构建SpringApplication对象。

创建SpringApplication对象:

2.1 PrimarySources 不为空,将启动类赋值给primarySources 对象。

2.2 从classpath类路径推断Web应用类型,有三种Web应用类型NONE、SERVLET、REACTIVE

2.3 初始化bootstrapRegistryInitializers

2.4 初始化ApplicationContextInitializer集合

2.5 初始化ApplicationListener

2.6 获取StackTraceElement数组遍历,通过反射获取堆栈中有main方法A的。 3、调用SpringBootApplication的run方法。 4、long startTime = System.nanoTime();

记录项目启动时间。 5、通过BootstrapRegistryInitializer来初始化DefaultBootstrapContext

6、getRunListeners(args)获取SpringApplicationRunListeners监听器 7、

listeners.starting()触发ApplicationStartingEvent事件

8、prepareEnvironment(listeners, bootstrapContext,

applicationArguments) 将配置文件读取到容器中,返回ConfigurableEnvironment 对象。

9、printBanner(environment) 打印Banner图,即SpringBoot启动时的图案。

10、根据WebApplicationType从ApplicationContextFactory工厂创建ConfigurableApplicationContext,并设置ConfigurableApplicationContext中的ApplicationStartup为DefaultApplicationStartup

11、

调用prepareContext()初始化context等,打印启动日志信息,启动Profile日志信息,并为BeanFactory中的部分属性赋值。

12、刷新容器,在该方法中集成了Tomcat容器 13、加载SpringMVC.

14、刷新后的方法,空方法,给用户自定义重写afterRefresh() 15、Duration timeTakenToStartup =

Duration.ofNanos(System.nanoTime() - startTime)算出启动花费的时间。

16、打印日志Started xxx in xxx seconds (JVM running for xxxx)

17、listeners.started(context,

timeTakenToStartup)触发ApplicationStartedEvent事件监听。上下文已刷新,应用程序已启动。

18、调用ApplicationRunner和CommandLineRunner 19、返回上下文。

相关推荐
苹果醋31 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
Wx-bishekaifayuan1 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml42 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
LuckyLay3 小时前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
小码编匠3 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
AskHarries4 小时前
Java字节码增强库ByteBuddy
java·后端
佳佳_4 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端