SpringBoot启动原理(基于2.3.9.RELEASE版本)

时间:2021-05-03 12:15:50   收藏:0   阅读:0

版本

以下源码的 SpringBoot 版本:2.3.9.RELEASE。

总体上

分为两大步:

main方法上的注解:@SpringBootApplication

源码

三个注解核心注解:@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan

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

@SpringBootConfiguration

根据Javadoc可知,该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration@ComponentScan处理,本质上与@Configuration注解没有区别。

@ComponentScan

扫描的 Spring 对应的组件,如 @Componet@Repository

我们可以通过 basePackages 等属性来细粒度的定制 @ComponentScan 自动扫描的范围,如果不指定,则默认Spring框架实现会从声明 @ComponentScan 所在类的package进行扫描,所以 SpringBoot启动类最好是放在根package下,我们自定义的类就放在对应的子package下,这样就可以不指定 basePackages

@EnableAutoConfiguration

@EnableAutoConfiguration总结

源码

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

SpringBoot 自动装配的核心注解,在 Spring 框架中就提供了各种以@Enable开头的注解,例如: @EnableCircuitBreaker@EnableScheduling等;

@EnableAutoConfiguration借助@Import的支持,收集和注册特定场景相关的bean定义;

自动装配的类,通常是 @Configuration 类,通过 SpringFactoriesLoader 加载到 Spring 容器。

@AutoConfigurationPackage

注册当前启动类的根package

注册 org.springframework.boot.autoconfigure.AutoConfigurationPackagesBeanDefinition

AutoConfigurationImportSelector

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

main方法

例子

main方法里调用org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)方法

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

SpringApplication#run

调用另外一个同名的重载方法run

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

实例化SpringApplication对象

  1. 首先会实例化SpringApplication一个对象
  2. 构造方法里初始化一些属性,比如webApplicationType,比如"SERVLET",初始化一些listeners
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
 return new SpringApplication(primarySources).run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 this.resourceLoader = resourceLoader;
 Assert.notNull(primarySources, "PrimarySources must not be null");
 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 初始化webApplicationType,比如"SERVLET"
 this.webApplicationType = WebApplicationType.deduceFromClasspath();
 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 初始化一些listeners
 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
 this.mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication#run(java.lang.String...)源码解析

经典的观察者模式,只要你把事件广播的顺序理解了,那整个流程就很容易串起来了:

  1. 创建一个StopWatch实例,用来记录SpringBoot的启动时间
  2. 通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
  3. 发布SprintBoot开始启动事件(EventPublishingRunListener#starting()
  4. 创建和配置environment(environmentPrepared()
  5. 打印SpringBoot的banner和版本
  6. 创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
  7. prepareContext
    1. 准备ApplicationContext,Initializers设置到ApplicationContext(contextPrepared())
    2. 打印启动日志,打印profile信息(如dev, test, prod)
    3. 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作,以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成(contextLoaded())
  8. afterRefresh hook方法
  9. stopWatch停止计时,日志打印总共启动的时间
  10. 发布SpringBoot程序已启动事件(started())
  11. 调用ApplicationRunner和CommandLineRunner
  12. 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了(running())
public ConfigurableApplicationContext run(String... args) {
    // 创建一个StopWatch实例,用来记录SpringBoot的启动时间
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 ConfigurableApplicationContext context = null;
 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 configureHeadlessProperty();
    // 通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
 SpringApplicationRunListeners listeners = getRunListeners(args);
    // 发布SprintBoot启动事件:ApplicationStartingEvent
 listeners.starting();
 try {
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     // 创建和配置environment,发布事件:SpringApplicationRunListeners#environmentPrepared
  ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  configureIgnoreBeanInfo(environment);
     // 打印SpringBoot的banner和版本
  Banner printedBanner = printBanner(environment);
     // 创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
  context = createApplicationContext();
  exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    new Class[] { ConfigurableApplicationContext.class }, context);
     // 准备ApplicationContext,Initializers设置到ApplicationContext后发布事件:ApplicationContextInitializedEvent
     // 打印启动日志,打印profile信息(如dev, test, prod)
     // 调用EventPublishingRunListener发布ApplicationContext加载完毕事件:ApplicationPreparedEvent
  prepareContext(context, environment, listeners, applicationArguments, printedBanner);
     // 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作
     // 以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
  refreshContext(context);
     // hook方法
  afterRefresh(context, applicationArguments);
     // stopWatch停止计时,日志打印总共启动的时间
  stopWatch.stop();
  if (this.logStartupInfo) {
   new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  }
     // 发布SpringBoot程序已启动事件ApplicationStartedEvent
  listeners.started(context);
     // 调用ApplicationRunner和CommandLineRunner
  callRunners(context, applicationArguments);
 }
 catch (Throwable ex) {
  handleRunFailure(context, ex, exceptionReporters, listeners);
  throw new IllegalStateException(ex);
 }

 try {
     // 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
  listeners.running(context);
 }
 catch (Throwable ex) {
  handleRunFailure(context, ex, exceptionReporters, null);
  throw new IllegalStateException(ex);
 }
 return context;
}

SpringBoot启动事件

EventPublishingRunListener

方法说明如下:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

 private final SpringApplication application;

 private final String[] args;

 private final SimpleApplicationEventMulticaster initialMulticaster;

 public EventPublishingRunListener(SpringApplication application, String[] args) {
  this.application = application;
  this.args = args;
  this.initialMulticaster = new SimpleApplicationEventMulticaster();
  for (ApplicationListener<?> listener : application.getListeners()) {
   this.initialMulticaster.addApplicationListener(listener);
  }
 }

 @Override
 public int getOrder() {
  return 0;
 }

    // SpringBoot启动事件
 @Override
 public void starting() {
  this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
 }

    // 创建和配置环境
 @Override
 public void environmentPrepared(ConfigurableEnvironment environment) {
  this.initialMulticaster
    .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
 }

    // 准备ApplicationContext
 @Override
 public void contextPrepared(ConfigurableApplicationContext context) {
  this.initialMulticaster
    .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
 }

    // 发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
 @Override
 public void contextLoaded(ConfigurableApplicationContext context) {
  for (ApplicationListener<?> listener : this.application.getListeners()) {
   if (listener instanceof ApplicationContextAware) {
    ((ApplicationContextAware) listener).setApplicationContext(context);
   }
   context.addApplicationListener(listener);
  }
  this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
 }

    // SpringBoot已启动事件
 @Override
 public void started(ConfigurableApplicationContext context) {
  context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
  AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
 }

    // "SpringBoot现在可以处理接受的请求"事件
 @Override
 public void running(ConfigurableApplicationContext context) {
  context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
  AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
 }

 @Override
 public void failed(ConfigurableApplicationContext context, Throwable exception) {
  ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
  if (context != null && context.isActive()) {
   // Listeners have been registered to the application context so we should
   // use it at this point if we can
   context.publishEvent(event);
  }
  else {
   // An inactive context may not have a multicaster so we use our multicaster to
   // call all of the context‘s listeners instead
   if (context instanceof AbstractApplicationContext) {
    for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
      .getApplicationListeners()) {
     this.initialMulticaster.addApplicationListener(listener);
    }
   }
   this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
   this.initialMulticaster.multicastEvent(event);
  }
 }

 private static class LoggingErrorHandler implements ErrorHandler {

  private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

  @Override
  public void handleError(Throwable throwable) {
   logger.warn("Error calling ApplicationEventListener", throwable);
  }

 }

}

SpringIOC 容器初始化过程

由于现在大都是用SpringBoot开发,所以呢,Spring IOC 初始化的源码,就是AnnotationConfigApplicationContext中的源码,IOC的初始化就是该类实例创建的过程。

创建的过程(AnnotationConfigApplicationContext的构造方法),由于debug过这个源码我个人把它分为两大步(暂时我先写出我的总结,后续看是否有时间能写一篇关于debug的过程):

  1. 给我们的Bean,创建与之对应的BeanDefinition,然后把他们放入ConcurrentHashMap(key:beanName和value:beanDefinition)中;BeanDefinition实际上包括一些Bean的信息,比如BeanName, Scope, 是否被@Primary注解修饰,是否是@Lazy,以及@Description等注解
  2. refresh()方法: 创建IOC需要的资源
评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!