唐僧喜欢小龙女

导航

Springboot的启动过程总结

1、总体步骤分两个

  • 创建Spring 应用(创建SpringApplication)
  • 运行Spring 应用(运行SpringApplication)

2、创建Spring 应用(创建SpringApplication)

就是new 一个对象,构造器里面做一些初始化的动作

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判断应用的类型deduce v.  推论; 推断; 演绎;的意思
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //设置初始化器,保存起来 从spring.factories 找
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设置监听器、保存起来从spring.factories 找
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //主应用类是那个
    this.mainApplicationClass = deduceMainApplicationClass();
  }

3、运行Spring 应用(运行SpringApplication)

// args 就是我们java-jar 时传的命令行参数
public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  //设置应用进入Headless 模式
  configureHeadlessProperty();
  //获取所有的运行监听器:就一个EventPublishingRunListener从spring.factories 中找的
  SpringApplicationRunListeners listeners = getRunListeners(args);
  //通知对系统启动过程中的人,项目正在启动,让所有的监听器感知到项目正在启动,这里就包括我们自己定义的监听器
  listeners.starting();
  try {
   //保存我们传过来的命令行参数
   ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
   //准备环境,
   ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
   configureIgnoreBeanInfo(environment);
   Banner printedBanner = printBanner(environment);
   //创建IOC容器,这里至关重要。这个没有特别的,根绝应用的类型来创建一个SERVLET容器
   context = createApplicationContext();
   exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
     new Class[] { ConfigurableApplicationContext.class }, context);
   //准备ioc 容器
   prepareContext(context, environment, listeners, applicationArguments, printedBanner);
   //刷新ioc 容器
   refreshContext(context);

   afterRefresh(context, applicationArguments);
   stopWatch.stop();
   if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
   }
   //通知所有的监听器应用启动完成了
   listeners.started(context);
   //获取容器中的ApplicationRunner 和CommandLineRunner ,然后遍历所有的runner,执行runner的run方法,runner一般都是我们自己写的
   callRunners(context, applicationArguments);
  }
  catch (Throwable ex) {
   handleRunFailure(context, ex, exceptionReporters, listeners);
   throw new IllegalStateException(ex);
  }

  try {
   //调用所有监听器的running 方法,对程序运行过程进行监听。
   listeners.running(context);
  }
  catch (Throwable ex) {
   handleRunFailure(context, ex, exceptionReporters, null);
   throw new IllegalStateException(ex);
  }
  return context;
}

 

 

3.1 prepareEnvironment 方法说明

   在这一步,SpringApplication会创建Spring启动所需的环境,这个环境主要由ConfigurableEnviroment对象表示。

   首先,该对象确认了程序是否需要设置Web环境,其次,该对象还确定了程序所需要的参数和读取的配置文件等信息。

/**
 *
 *  获取或者创建一个Environment对象,如果我们自己在main 方法里面创建了一个Environment,并放到
 *        SpringApplication 中了就不创建了,
 *        SpringApplication application = new SpringApplication(ProfileDemoApplication.class);
 *        ConfigurableEnvironment environment = new StandardEnvironment();
 *        environment.setActiveProfiles("dev");
 *        application.setEnvironment(environment);
 *  否则就创建
 *
 *
 */

注意:以下的说明
// 添加初始的properties(注意:当前并未加载如application.properties/yml的properties)
// 添加初始的profile(注意:当前并未加载如application.properties/yml配置profile)
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        //创建或者获取一个Environment 对象
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //配置environment
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        // 触发监听器(主要是触发ConfigFileApplicationListener,这个监听器将会加载如application.properties/yml这样的配置文件)
        //通知所有的监听器环境准备好了,包括我门自己定义的监听器。
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
     
        return environment;
    }

 3.1.1 configureEnvironment

    //配置环境
    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService)conversionService);
        }
        //把命令行参数、外部配置文件的属性(利用@PropertySource指定的文件),系统属性都配置到环境里面、加载配置文件
        this.configurePropertySources(environment, args);
        //激活profile环境的
        this.configureProfiles(environment, args);
    }

 3.2 prepareContext

/**
 * 
 * 
 * 虽然已经得到了ApplicationContext对象,但此时的对象还只是一个空白对象,需要准备和处理后,ApplicationContext才能被使用。
 *
 * 在准备过程中主要做了做了几件事:
 * 为ApplicationContext设置之前准备好的Environment对象、也就是说把Environment 组件放到容器里面
 *
 * 通过对ApplicationContext后置处理或是BeanDefinitionLoader等方式往容器中添加一些初始的Bean。
 *
 * 应用默认的初始化器初始化应用程序上下文(责任链模式的应用,多个初始化器形成一个List,应用程序需要被每个初始化器应用一次,每个初始化器有自己的职责)。
 *
 * 准备过程中ApplicationRunListener发出两个消息,分别是contextPrepared和contextLoaded。
 */

 

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 给上下文对象设置环境对象,给AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner设置环境对象
    context.setEnvironment(environment);
    // 1. 上下文后置处理,包括向BeanFactory中注册BeanNameGenerator和ConversionService
    postProcessApplicationContext(context);
    // 2. SpringApplication构造器中初始化了各种ApplicationContextInitializer(应用初试化器),
    //循环调用他们的initialize方法、对ioc 容器进行扩展
    applyInitializers(context);
    // 遍历所有的监听器,通知监听器上下文准备好了。
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        // 打印启动信息,包括pid,用户等
        logStartupInfo(context.getParent() == null);
        // 答应profile信息
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 将命令行参数组件注册到容器里面
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        // 将Banner注册到容器中
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        // 设置不允许定义同名的BeanDefinition,重复注册时抛出异常
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        // 如果懒加载,添加懒加载后置处理器
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    //遍历所有的监听器,IOC容器加载好了
    listeners.contextLoaded(context);
}

3.3 refreshContext()——刷新上下文

在应用程序上下文准备好后,可以通过刷新应用程序上下文发现Bean并加载到容器中,主要是目的是加载bean
refreshContext()会调用ApplicationContext.refresh()方法。

AbstractApplicationContext中定义了refresh()方法的基本框架(模板模式的应用)。

3.3.1 refresh()

这个是spring经典的容器初始化过程,创建所有的组件

 

 

posted on 2021-07-17 11:49  与时具进&不忘初心  阅读(136)  评论(0编辑  收藏  举报