学习过 springboot 的都知道, 在 Springboot 的 main 入口函数中调用 SpringApplication.run(DemoApplication.class,args) 函数便可以启用 SpringBoot 应用程序, 跟踪一下 SpringApplication 源码可以发现, 最终还是调用了 SpringApplication 的动态 run 函数.
下面以 SpringBoot2.0.3.RELEASE 为例简单分析一下运行过程.
SpringApplicatiton 部分源码:
- public static ConfigurableApplicationContext run(Class<?>[] primarySources,
- String[] args) {
- // 创建 springapplication 对象, 调用函数 run(args)
- return new SpringApplication(primarySources).run(args);
- }
上面的源码可以发现还是先创建 SpringApplication 实例, 再调用 run 方法
第一步 分析 SpringApplication 构造函数
SpringApplication 构造函数代码如下:
- public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
- this.resourceLoader = resourceLoader;
- Assert.notNull(primarySources, "PrimarySources must not be null");
- this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
- //1: 判断 web 环境
- this.webApplicationType = deduceWebApplicationType();
- //2: 加载 classpath 下 META-INF/spring.factories 中配置的 ApplicationContextInitializer
- setInitializers((Collection) getSpringFactoriesInstances(
- ApplicationContextInitializer.class));
- //3: 加载 classpath 下 META-INF/spring.factories 中配置的 ApplicationListener
- setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
- //4: 推断 main 方法所在的类
- this.mainApplicationClass = deduceMainApplicationClass();
- }
具体逻辑分析:
deduceWebApplicationType(), SpringApplication 构造函数中首先初始化应用类型, 根据加载相关类路径判断应用类型, 具体逻辑如下:
- private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
- + "web.reactive.DispatcherHandler";
- private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
- + "web.servlet.DispatcherServlet";
- private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
- "org.springframework.web.context.ConfigurableWebApplicationContext" };
- private WebApplicationType deduceWebApplicationType() {
- // 当类路径中存在 REACTIVE_WEB_ENVIRONMENT_CLASS 并且不存在 MVC_WEB_ENVIRONMENT_CLASS 时
- if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
- && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
- return WebApplicationType.REACTIVE;
- }
- // 当加载的类路径中不包含 WEB_ENVIRONMENT_CLASSES 中定义的任何一个类时, 返回标准应用
- for (String className : WEB_ENVIRONMENT_CLASSES) {
- if (!ClassUtils.isPresent(className, null)) {22 return WebApplicationType.NONE;
- }
- }
- // 加载的类路径中包含了 WEB_ENVIRONMENT_CLASSES 中定义的所有类型则判断为 web 应用
- return WebApplicationType.SERVLET;
- }
2. setInitializers 初始化属性 initializers, 加载 classpath 下 META-INF/spring.factories 中配置的 ApplicationContextInitializer, 此处 getSpringFactoriesInstances 方法入参 type=ApplicationContextInitializer.class
- private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
- Class<?>[] parameterTypes, Object... args) {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- // Use names and ensure unique to protect against duplicates
- // SpringFactoriesLoader.loadFactoryNames() 方法将会从 calssptah 下的 META-INF/spring.factories 中读取 key 为 //org.springframework.context.ApplicationContextInitializer 的值, 并以集合形式返回
- Set<String> names = new LinkedHashSet<>(
- SpringFactoriesLoader.loadFactoryNames(type, classLoader));
- // 根据返回 names 集合逐个实例化, 也就是初始化各种 ApplicationContextInitializer, 这些 Initializer 实际是在 Spring 上下文 ApplicationContext 执行 refresh 前调用
- List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
- classLoader, args, names);
- AnnotationAwareOrderComparator.sort(instances);
- return instances;
- }
3. setListeners 初始化属性 listeners, 加载 classpath 下 META-INF/spring.factories 中配置的 ApplicationListener, 此处入参为 getSpringFactoriesInstances 方法入参 type= ApplicationListener.class
- private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
- Class<?>[] parameterTypes, Object... args) {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- // Use names and ensure unique to protect against duplicates
- // SpringFactoriesLoader.loadFactoryNames() 方法将会从 calssptah 下的 META-INF/spring.factories 中读取 key 为 //org.springframework.context.ApplicationListener 的值, 并以集合形式返回
- Set<String> names = new LinkedHashSet<>(
- SpringFactoriesLoader.loadFactoryNames(type, classLoader));
- // 根据配置, 初始化各种 ApplicationListener, 作用是用来监听 ApplicationEvent
- List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
- classLoader, args, names);
- AnnotationAwareOrderComparator.sort(instances);
- return instances;
- }
第二步 分析 SpringApplication 中 run 方法
SpringApplication 的 run 方法代码如下:
- public ConfigurableApplicationContext run(String... args) {
- StopWatch stopWatch = new StopWatch();
- stopWatch.start();
- ConfigurableApplicationContext context = null;
- Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
- // 设置系统变量 java.awt.headless
- configureHeadlessProperty();
- //1: 加载 classpath 下面的 META-INF/spring.factories SpringApplicationRunListener
- SpringApplicationRunListeners listeners = getRunListeners(args);
- //2: 执行所有 runlistener 的 starting 方法, 实际上发布一个 ApplicationStartingEvent 事件
- listeners.starting();
- try {
- //3: 实例化 ApplicationArguments 对象
- ApplicationArguments applicationArguments = new DefaultApplicationArguments(
- args);
- //4: 创建 Environment (web 环境 or 标准环境)+ 配置 Environment, 主要是把 run 方法的参数配置到 Environment
- ConfigurableEnvironment environment = prepareEnvironment(listeners,
- applicationArguments);
- configureIgnoreBeanInfo(environment);
- // 打印 banner,SpringBoot 启动时, 控制台输出的一个歪歪扭扭的很不清楚的 Spring 几个大字母, 也可以自定义, 参考博客: http://majunwei.com/view/201708171646079868.html
- Banner printedBanner = printBanner(environment);
- //5: 根据不同 environment 实例化 context
- context = createApplicationContext();
- // 异常处理
- exceptionReporters = getSpringFactoriesInstances(
- SpringBootExceptionReporter.class,
- new Class[] { ConfigurableApplicationContext.class }, context);
- //6: 上下文相关预处理
- prepareContext(context, environment, listeners, applicationArguments,
- printedBanner);
- //7: 执行 context 的 refresh, 并且调用 context 的 registerShutdownHook 方法
- refreshContext(context);
- //8: 空方法
- afterRefresh(context, applicationArguments);
- stopWatch.stop();
- if (this.logStartupInfo) {
- new StartupInfoLogger(this.mainApplicationClass)
- .logStarted(getApplicationLog(), stopWatch);
- }
- //9: 执行所有 runlisteners 的 started 方法, 发布 ApplicationStartedEvent 事件
- listeners.started(context);
- //10: 遍历执行 CommandLineRunner 和 ApplicationRunner
- // 如果需要在 SpringBoot 应用启动后运行一些特殊的逻辑, 可以通过实现 ApplicationRunner 或 CommandLineRunner 接口中的 run 方法, 该自定义类的 run 方法会在此处统一调用
- callRunners(context, applicationArguments);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, exceptionReporters, listeners);
- throw new IllegalStateException(ex);
- }
- try {
- listeners.running(context);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, exceptionReporters, null);
- throw new IllegalStateException(ex);
- }
- return context;
- }
具体分析:
1. getRunListeners(args) 加载各种 SpringApplicationRunListener 实例, 内部实现也还是通过 SpringFactoriesLoader.loadFactoryNames(type, classLoader)) 实现, 加载 META-INF/spring.factories 中 key 为 org.springframework.boot.SpringApplicationRunListener 的值, 生成对应实例.
2. listeners.starting() 执行所有 SpringApplicationRunListener 的 stating 方法, 发布 ApplicationStartedEvent 事件, 该事件被 ApplicationListener 类型的 listener 监听
3. 实例化 ApplicationArguments 对象
4 . 配置环境并发布 ApplicationEnvironmentPreparedEvent 事件
- private ConfigurableEnvironment prepareEnvironment(
- SpringApplicationRunListeners listeners,
- ApplicationArguments applicationArguments) {
- // Create and configure the environment
- ConfigurableEnvironment environment = getOrCreateEnvironment();
- //configureEnvironment 配置 properties 和 profiles
- configureEnvironment(environment, applicationArguments.getSourceArgs());
- // 执行 EventPublishingRunListener 发布 ApplicationEnvironmentPreparedEvent 事件, 将会被 ApplicationListener 监听到
- listeners.environmentPrepared(environment);
- //
- bindToSpringApplication(environment);
- if (this.webApplicationType == WebApplicationType.NONE) {
- environment = new EnvironmentConverter(getClassLoader())
- .convertToStandardEnvironmentIfNecessary(environment);
- }
- ConfigurationPropertySources.attach(environment);
- return environment;
- }
备注: 实际上载 spring-boot-2.0.3.RELEASE.jar 包中, 可以发现 spring.factories 中只配置了一个 RunListener: org.springframework.boot.context.event.EventPublishingRunListener
截取 EventPublishingRunListener.java 部分代码:
- public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
- public EventPublishingRunListener(SpringApplication application, String[] args) {
- this.application = application;
- this.args = args;
- this.initialMulticaster = new SimpleApplicationEventMulticaster();
- // 将 SpringApplication 实例中的 ApplicationListener 类型的 listeners 添加到 initialMulticaster, 后续执行监听
- for (ApplicationListener<?> listener : application.getListeners()) {
- this.initialMulticaster.addApplicationListener(listener);
- }
- }
- // 发布一个 ApplicationEnvironmentPreparedEvent 事件
- @Override
- public void environmentPrepared(ConfigurableEnvironment environment) {
- // 所有被添加到 initialMulticaster 中的 listener 都将监听 ApplicationEnvironmentPreparedEvent 事件
- this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
- this.application, this.args, environment));
- }
- }
5. 根据 environment 类型创建 ApplicationContext
6. 上下文相关处理:
- private void prepareContext(ConfigurableApplicationContext context,
- ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
- ApplicationArguments applicationArguments, Banner printedBanner) {
- context.setEnvironment(environment);
- // 配置 beanNameGenerator 和资源加载器
- postProcessApplicationContext(context);
- // 回调所有的 ApplicationContextInitializer
- applyInitializers(context);
- // 执行所有 SpringApplicationRunListener 的 contextPrepared 方法, 触发事件, 实际上 EventPublishingRunListener 中 contextPrepared 是一个空方法, 什么都没执行
- listeners.contextPrepared(context);
- if (this.logStartupInfo) {
- logStartupInfo(context.getParent() == null);
- logStartupProfileInfo(context);
- }
- // 向 Spring 容器注入 springApplicationArguments 和 springBootBanner
- // Add boot specific singleton beans
- context.getBeanFactory().registerSingleton("springApplicationArguments",
- applicationArguments);
- if (printedBanner != null) {
- context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
- }
- // Load the sources
- Set<Object> sources = getAllSources();
- Assert.notEmpty(sources, "Sources must not be empty");
- load(context, sources.toArray(new Object[0]));
- // 执行所有 SpringApplicationRunListener 的 contextLoaded 方法, 下面是 EventPublishingRunListener 中的 contextLoaded
- listeners.contextLoaded(context);
- }
EventPublishingRunListener.java 中 contextLoaded 方法具体实现
- public void contextLoaded(ConfigurableApplicationContext context) {
- for (ApplicationListener<?> listener : this.application.getListeners()) {
- if (listener instanceof ApplicationContextAware) {
- ((ApplicationContextAware) listener).setApplicationContext(context);
- }
- context.addApplicationListener(listener);
- }
- // 触发 ApplicationPreparedEvent 事件, ApplicationListener 负责监听
- this.initialMulticaster.multicastEvent(
- new ApplicationPreparedEvent(this.application, this.args, context));
- }
7. 执行 context 的 refresh, 并且调用 context 的 registerShutdownHook 方法
8. afterRefresh 空方法
9. 执行所有 runlisteners 的 started 方法, 发布 ApplicationStartedEvent 事件
10. 遍历执行 CommandLineRunner 和 ApplicationRunner
以上.
来源: https://www.cnblogs.com/ashleyboy/p/9563565.html