[SpringBoot源码分析]SpringBoot是如何启动的

 2023-02-03
原文作者:积跬步以至千里 原文地址:https://juejin.cn/post/7032598546360729614

SpringBoot概念

  • SpringApplication提供了从main方法开始的启动spring应用程序的便捷方法。
  • Spring Boot 基于 spring4.0以后

特点

  • 快速开发和构建微服务系统
  • 内嵌容器,如 Tomcat、Undertow
  • 自动加载
  • 自动管理依赖

引言

  • 那么上述的springboot有这么多好处,那先研究下springboot是怎么启动,后续我们再分篇研究下自动加载

开始分析源码

    @SpringBootApplication(scanBasePackages = {"com.github.demo"})
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    // SpringApplication的run方法
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
       return run(new Class<?>[] { primarySource }, args);
    }
    // 最终调用的SpringApplication的run方法
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
       return new SpringApplication(primarySources).run(args);
    }

初始化SpringApplication

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
       // 设置 resource
       this.resourceLoader = resourceLoader;
       Assert.notNull(primarySources, "PrimarySources must not be null");
       
       this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
       // 初始化类型 是 web 还是 reactor
       this.webApplicationType = WebApplicationType.deduceFromClasspath();
       
       // 从类路径下找到META‐INF/spring.factories文件中,加载ApplicationContextInitializer接口的实现类, 并设置
       setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
       
        // 从类路径下找到META‐INF/spring.factories文件中,ApplicationListener接口的实现类,并设置
       setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
       this.mainApplicationClass = deduceMainApplicationClass();
    }

ApplicationContextInitializer

定义

Spring容器刷新之前,初始化ConfigurableApplicationContext上下文的回调接口 ApplicationContextInitializer是Spring框架原有的产物

实现类
    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
    org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

ApplicationListener

定义

ApplicationListener:监听spring容器中发布的事件

实现类
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.context.logging.LoggingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

核心的运行方法

    // 运行springboot应用, 创建并且刷新ApplicationContext.
    public ConfigurableApplicationContext run(String... args) {
       // 1.计时器   非重点
       StopWatch stopWatch = new StopWatch();
       stopWatch.start();
       
       ConfigurableApplicationContext context = null;
       // 2.支持报告spring应用启动错误  非重点
       Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
       configureHeadlessProperty();
       
       // 3.获取listener   重点
       SpringApplicationRunListeners listeners = getRunListeners(args);
       // 3.1 调用启动中事件的方法 重点
       listeners.starting();
       
       try {
           // 4. ApplicationArguments  重点
          ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          // 5. 预处理环境参数 重点
          ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
          configureIgnoreBeanInfo(environment);
          // 6. 打印Banner 非重点
          Banner printedBanner = printBanner(environment);
          
          // 7.创建ApplicationContext
          context = createApplicationContext();
          
          // 8.通过spi获取错误报告类 非重点
          exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
          // 9. 预处理上下文       重点
          prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          // 10. 刷新上下文 重点
          refreshContext(context);
          // 11. 后置处理 重点
          afterRefresh(context, applicationArguments);
          // 12. 计时器处理 非重点
          stopWatch.stop();
          if (this.logStartupInfo) {
             new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
          }
          // 13. 调用启动事件的方法 重点
          listeners.started(context);
          // 14. 调用实现ApplicationRunner和CommandLineRunner的方法  重点
          callRunners(context, applicationArguments);
       }
       catch (Throwable ex) {
         // 15.异常处理 重点
          handleRunFailure(context, ex, exceptionReporters, listeners);
          throw new IllegalStateException(ex);
       }
    
       try {
          // 16. 调用运行中事件 重点
          listeners.running(context);
       }
       catch (Throwable ex) {
       	// 17 异常处理 重点
          handleRunFailure(context, ex, exceptionReporters, null);
          throw new IllegalStateException(ex);
       }
       return context;
    }

解释 3.获取事件监听

    private SpringApplicationRunListeners getRunListeners(String[] args) {
       Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
       return new SpringApplicationRunListeners(logger,
             getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

注:通过SPI加载SpringApplicationRunListener接口的所有实现

SpringApplicationRunListener 生命周期

定义

spring应用运行方法的收听者

7个阶段

    default void starting() {
    }
    default void environmentPrepared(ConfigurableEnvironment environment) {
    }
    
    default void contextPrepared(ConfigurableApplicationContext context) {
    }
    
    default void contextLoaded(ConfigurableApplicationContext context) {
    }
    
    default void started(ConfigurableApplicationContext context) {
    }
    
    default void running(ConfigurableApplicationContext context) {
    }
    
    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }
实现类

在spring.factories文件定义了SpringApplicationRunListener的实现类

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener类的实现接口的七个阶段
    @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));
    }
    
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
       this.initialMulticaster
             .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    }
    
    @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));
    }
    
    @Override
    public void started(ConfigurableApplicationContext context) {
       context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    }
    
    @Override
    public void running(ConfigurableApplicationContext context) {
       context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
    }
    
    @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);
       }
    }

注:EventPublishingRunListener类的实现其实就是在不同阶段广播不同的事件

CommandLineRunner

定义

属于springboot应用的扩展点,在springboot应用的applicationContext初始化后开始被执行的

    @FunctionalInterface
    public interface CommandLineRunner {
    
       /**
        * Callback used to run the bean.
        * @param args incoming main method arguments
        * @throws Exception on error
        */
       void run(String... args) throws Exception;
    
    }

ApplicationRunner

定义

    @FunctionalInterface
    public interface ApplicationRunner {
    
       /**
        * Callback used to run the bean.
        * @param args incoming application arguments
        * @throws Exception on error
        */
       void run(ApplicationArguments args) throws Exception;
    
    }

构建ApplicationArguments

    public interface ApplicationArguments {
        String[] getSourceArgs();
        Set<String> getOptionNames();
        boolean containsOption(String name);
        List<String> getOptionValues(String name);
        List<String> getNonOptionArgs();
    }
    public class DefaultApplicationArguments implements ApplicationArguments {
    
    	private final Source source;
    
    	private final String[] args;
    
    	public DefaultApplicationArguments(String... args) {
    		Assert.notNull(args, "Args must not be null");
    		this.source = new Source(args);
    		this.args = args;
    	}
    	......
    	private static class Source extends SimpleCommandLinePropertySource {
    
    		Source(String[] args) {
    			super(args);
    		}
    
    	}
    
    }

创建ApplicationContext

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
    		+ "annotation.AnnotationConfigApplicationContext";
    
    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
    		+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
    		+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
    
    protected ConfigurableApplicationContext createApplicationContext() {
    	Class<?> contextClass = this.applicationContextClass;
    	if (contextClass == null) {
    		try {
    			switch (this.webApplicationType) {
    			case SERVLET:
    				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
    				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);
    }

注:

  • 这个比较简单,就是通过org.springframework.boot.WebApplicationType创建不同的ApplicationContext

预处理上下文(prepareContext方法)

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    		// 设置容器的变量
    		context.setEnvironment(environment);
    		// 执行容器的后置处理
    		postProcessApplicationContext(context);
    		// 在上下文刷新之前,将ApplicationContextInitializer应用到上下文中。
    		applyInitializers(context);
    		// 广播上下文已经准备好的事件
    		listeners.contextPrepared(context);
    		
    		if (this.logStartupInfo) {
    			logStartupInfo(context.getParent() == null);
    			logStartupProfileInfo(context);
    		}
    		// Add boot specific singleton beans
    		// 在容器中注入applicationArguments 的bean
    		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    		if (printedBanner != null) {
    			beanFactory.registerSingleton("springBootBanner", printedBanner);
    		}
    		if (beanFactory instanceof DefaultListableBeanFactory) {
    			((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]));
    		// 广播容器已加载的事件
    		listeners.contextLoaded(context);
    	}

刷新上下文

    private void refreshContext(ConfigurableApplicationContext context) {
    		if (this.registerShutdownHook) {
    			try {
    				// 注册容器关闭之前做一些资源回收的操作
    				context.registerShutdownHook();
    			}
    			catch (AccessControlException ex) {
    				// Not allowed in some environments.
    			}
    		}
    		refresh(context);
    	}

真正调用AbstractApplicationContext#refresh

    protected void refresh(ApplicationContext applicationContext) {
    		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    		((AbstractApplicationContext) applicationContext).refresh();
    	}

AbstractApplicationContext#refresh 这个方法主要是spring应用上下文生命周期的方法,这里只做简单的介绍,不往深处深究了

public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 留给子类去实现创建BeanFactory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    		// 预处理BeanFactory对象
    		prepareBeanFactory(beanFactory);
    
    		try {
    			// 允许子类中对BeanFactory进行后期处理.
    			postProcessBeanFactory(beanFactory);
    
    			// I调用注册为BeanFactory的工厂处理器
    			invokeBeanFactoryPostProcessors(beanFactory);
    
    			// 注册实现BeanPostProcessor接口的bean后置处理类
    			registerBeanPostProcessors(beanFactory);
    
    			// 初始化application实现广播器
    			initApplicationEventMulticaster();
    
    			// 又是留给子类实现刷新操作额
    			onRefresh();
    
    			// 检查bean的监听器并注册它们。.
    			registerListeners();
    
    			//完成Bean的初始化
    			finishBeanFactoryInitialization(beanFactory);
    
    			// 发布上下文刷新完成的时间
    			finishRefresh();
    		}catch (BeansException ex) {
    			// Propagate exception to caller.
    			throw ex;
    		}finally {
    			resetCommonCaches();
    		}
    	}
    }

后置处理

    protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    	}

这里是空方法,需要子类自己实现

调用启动事件的方法

    void started(ConfigurableApplicationContext context) {
    		for (SpringApplicationRunListener listener : this.listeners) {
    			listener.started(context);
    		}
    	}

调用实现ApplicationRunner和CommandLineRunner的方法

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
    		List<Object> runners = new ArrayList<>();
    		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    		//排序
    		AnnotationAwareOrderComparator.sort(runners);
    		for (Object runner : new LinkedHashSet<>(runners)) {
    			if (runner instanceof ApplicationRunner) {
    				callRunner((ApplicationRunner) runner, args);
    			}
    			if (runner instanceof CommandLineRunner) {
    				callRunner((CommandLineRunner) runner, args);
    			}
    		}
    	}

总结

  • 运用了观察者模式
  • 运用SPI技术来动态加载具体的实现类,dubbo也是这样实现的,想了解dubbo的spi可以查看下面文章:Dubbo的SPI
  • SpringBoot启动加载器有两种方式:CommandLineRunner和ApplicationRunner,并且是在应用程序启动完成后
  • CommandLineRunner和ApplicationRunner的执行顺序,是将他们都统一添加到同一个list 然后根据@Order注解派去