springBoot 启动过程分析

一、springBoot生命周期伴随的事件

                                      

   springBoot的启动过程就是通过事件机制驱动的,可以通过监听这些事件做一些定制化的功能。因为springBoot已经成为众多中间件和框架的基础,所以了解其整个生命周期是很必要的,接下来就从源码的角度去分析它的整个启动流程。

二、源码分析

1、实例化SpringApplication

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));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

  构造方法里主要做了四件事:

    (1)根据classpath下是否存在某些特定类来确定应用的类型,包括:REACTIVE、SERVLET和NONE三种类型,其中REACTIVE、SERVLET会以WEB应用的形式启动,NONE则会在应用执行完毕后结束进程

    (2)利用SpringFactoriesLoader自动加载spring.factories文件中配置的 ApplicationContextInitializer,ApplicationContextInitializer原本是Spring框架Context模块下的一个接口,Spring Boot内部提供了一些该接口的实现类,使得我们有机会对ApplicationContext做一些特定的初始化工作

    (3)利用SpringFactoriesLoader自动加载spring.factories文件中配置的 ApplicationListener,ApplicationListener同样是Context模块下的接口,Spring Boot内部提供了一些该接口的实现类,使得我们有机会在监听到某些应用事件发生时做一些特定的工作。

    (4)根据main方法执行堆栈确定应用的主引导类,即Spring Boot项目自带的XXApplication类

2、进入到真正的run方法中

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
//在这实例化SpringApplicationRunListener SpringApplicationRunListeners listeners
= getRunListeners(args);
//发布ApplicationStartingEvent事件 listeners.starting();
try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args);
//发布ApplicationEnvironmentPreparedEvent事件 ConfigurableEnvironment environment
= prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);

              /**
               * 发布ApplicationContextInitializedEvent事件
               * load(context)方法加载所有bean定义信息(BeanDefinition)
               * 发布ApplicationPreparedEvent事件
               **/

            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
//发布ApplicationStartedEvent事件 listeners.started(context); callRunners(context, applicationArguments); }
catch (Throwable ex) {
//发布ApplicationFailedEvent事件 handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex); } try {
//发布ApplicationReadyEvent事件 listeners.running(context); }
catch (Throwable ex) {
//发布ApplicationFailedEvent事件 handleRunFailure(context, ex, exceptionReporters,
null); throw new IllegalStateException(ex); } return context; }

  (1)listeners.starting() —— 发布ApplicationStartingEvent事件

public void starting() {
        this.initialMulticaster.multicastEvent(
                new ApplicationStartingEvent(this.application, this.args));
    }

   监听此事件的监听器:

      LoggingApplicationListener:@since 2.0.0。对日志系统抽象LoggingSystem执行实例化以及初始化之前的操作,默认使用的是基于Logback的LogbackLoggingSystem

      BackgroundPreinitializer:启动一个后台进行对一些类进行预热。如ValidationInitializer、JacksonInitializer...,因为这些组件有第一次惩罚的特点(并且首次初始化均还比较耗时),所以使用后台线程先预热效果更佳

      DelegatingApplicationListener:它监听的是ApplicationEvent,而实际上只会ApplicationEnvironmentPreparedEvent到达时生效,所以此处忽略

      LiquibaseServiceLocatorApplicationListener:略

  (2)prepareEnvironment(listeners,applicationArguments) ——发布ApplicationEnvironmentPreparedEvent事件

   Environment代表着应用的上下文环境,主要是解析获取profile和properties。

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();  ——(1)
        configureEnvironment(environment, applicationArguments.getSourceArgs()); ——(2)
        listeners.environmentPrepared(environment); ——(3)
        bindToSpringApplication(environment); ——(4)
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment); ——(5)
        return environment;
    }

  主要做以下几件事情:

    1)根据webApplicationType创建不同的Environment,web环境下创建了StandardServletEnvironment

    2)将应用启动时候传入的args设置到Environment中去

    3)发布ApplicationEnvironmentPreparedEvent事件,告诉之前获取的所有监听器,应用的上下文环境已经准备好了,这个时候监听器主要做的事情就是加载application.properties以及profile文件

    4)将获取到的environment中的spring.main配置绑定到SpringApplication的source中

    5)将创建好的Environment里获取到的MutablePropertySources再设置到configurationProperties属性值里面去,并且作为第一个元素。

   (3)createApplicationContext()

  终于到了创建应用上下文的时候了,前面都是在做准备工作,准备工作做好了,开始创建Spring中最核心的容器ApplicationContext了。

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); ——(1)
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); ——(2)
    }

  主要做了两件事:

    1)加载ApplicationContext的class对象,如果是web应用加载的就是"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext",这里说下Class.forName和ClassLoader的区别:

      Class.forName除了将类的.class文件加载到jvm外,还会对类进行解释,执行static方法。

    2)对类—AnnotationConfigServletWebServerApplicationContext进行初始化

public AnnotationConfigServletWebServerApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

    进行初始化时会创建AnnotatedBeanDefinitionReader reader和ClassPathBeanDefinitionScanner scaner,扫描所有带@Component、@Service、@Repository、@Controller等注解的类,注册到容器中。

    同时也会创建上下文的beanFactory,实际类型为DefaultListableBeanFactory。如下是其类图关系:

                           

     初始化子类之前必须先初始化其父类,在父类GenericApplicationContext中创建beanFactory:

public GenericApplicationContext() {
        this.beanFactory = new DefaultListableBeanFactory();
    }

   (4)prepareContext()

    将之前获取到的Environment、listeners、applicationArguments以及printBanner都设置到applicationContext中去,并发布ApplicationContextInitializedEvent事件和ApplicationPreparedEvent事件。

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
//在此处发布ApplicationContextInitializedEvent listeners.contextPrepared(context);
if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0]));
//在此处发布ApplicationPreparedEvent listeners.contextLoaded(context); }

 

  

 

   

 

posted @ 2021-02-08 11:03  jingyi_up  阅读(126)  评论(0编辑  收藏  举报