SpringBoot启动流程源码分析

版本介绍

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

启动类

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

构造方法

我们进入SpringApplication类中,以下是两个构造方法

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	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));
        //设置监听器
        //加载所以ApplicationListener.class类型的监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //确定主类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

构造方法中一些重要的初始化步骤

//判断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
//加载所以ApplicationListener.class类型的监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//确定主类
this.mainApplicationClass = deduceMainApplicationClass();

接下来一个一个具体分析

WebApplicationType.deduceFromClasspath()

简述一下,就是有三种应用类型,分别为

NONE : 该应用不应该作为一个web应用运行,并且不应该启动一个嵌入式web服务。

The application should not run as a web application and should not start an
embedded web server.

SERVLET:该应用应该运行一个基于servlet的web应用,并且启动一个嵌入式servlet web服务。同步阻塞式 tomcat

The application should run as a servlet-based web application and should start an
embedded servlet web server.

REACTIVE:该应用应该运行一个reactive web应用,并且应该启动一个嵌入式reactive web服务。异步非阻塞式

The application should run as a reactive web application and should start an
embedded reactive web server.

反应式web应用,实际就是web客户端和后端会有一个管道,后端将改变数据不断的推送到客户端,而不是传统的WEB应用将后端数据获得后发送给客户端显示,这样前端就被阻塞必须等待后端数据处理完成才能显示,反应式编程是依据观察者和订阅者模式来实现的,这样缩短了客户端的响应时间,单因为客户端的不确定性,可能前端很容易就被后端推送的数据压垮,所以有了被压(back pressure)概念,直到Jdk 9才对reactive有了支持,spring 5的flux就是一个reactive框架。

参考:Reactive 栈的 Web 应用

	static WebApplicationType deduceFromClasspath() {
//如果当前包下存在WEBFLUX_INDICATOR_CLASS,并且不存在WEBMVC_INDICATOR_CLASS和JERSEY_INDICATOR_CLASS,则为反应式类型
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
//若SERVLET_INDICATOR_CLASSES的类都不存在,则NONE
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

setInitializers();

ApplicationContextInitializer是用于在刷新容器之前初始化ConfigurableApplicationContext的回调接口,我们也可以实现ApplicationContextInitializer接口来在容器刷新之前做一些我们的初始化逻辑。

  1. 通过SPI机制加载所有类型为ApplicationContextInitializer的组件,并创建对应的实例
  2. 把创建出来的实例设置到initializers属性中
//核心方法
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//创建类加载器
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
//加载工厂名字,存入集合set
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建spring工厂实例,并存入列表
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//获取工厂类型名
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
//读取META-INF下的spring.factories文件
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
//遍历url
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
//加载资源配置
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
//继续遍历配置
                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
//将类和相关信息存入result
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }
	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
//获取反射
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//创建实例
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}
//一些相关代码
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//不重要
	public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
		this.initializers = new ArrayList<>(initializers);
	}
//不重要
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
//这个接口继承了ConfigurableApplicationContext接口
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C var1);
}
//返回loadSpringFactories 如果这个key的value不为null或者有这个key,返回该键值对,否则返回默认值
    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }

setListeners();

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

核心方法与上方一致getSpringFactoriesInstances()

  1. 通过SPI机制加载所有类型为ApplicationListener的组件,并创建对应的实例
  2. 把创建出来的实例设置到listeners属性中
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}
public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp = System.currentTimeMillis();

    public ApplicationEvent(Object source) {
        super(source);
    }

    public final long getTimestamp() {
        return this.timestamp;
    }
}

deduceMainApplicationClass();

跟着当前方法的调用栈往上找,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。

	private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
//跟着当前方法的调用栈往上找,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

run方法

核心方法

  1. 创建时间监控器,并且计时
  2. 配置awt信息
  3. 获取监控,并且开始监控
  4. 创建并配置环境
  5. 若spring.beaninfo.ignore配置了,则加载配置
  6. 打印banner
  7. 创建上下文
  8. 初始化异常报告器
  9. 初始化ioc
  10. 刷新上下文
  11. 后置方法预留
  12. 停止计时
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
	public ConfigurableApplicationContext run(String... args) {
//创建时间性能监控器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
//创建空的IOC容器
		ConfigurableApplicationContext context = null;
//创建一组异常报告器
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置与awt相关的信息
		configureHeadlessProperty();
//获取SpringApplicationRunListeners 
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {//创建参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建并配置环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//如果配置了spring.beaninfo.ignore,则配置环境
			configureIgnoreBeanInfo(environment);
//打印banner
			Banner printedBanner = printBanner(environment);
//创建上下文对象
			context = createApplicationContext();
//初始化异常报告器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
//初始化IOC容器
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器上下文
			refreshContext(context);
//预留方法 目前什么也没有
			afterRefresh(context, applicationArguments);
//停止计时
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			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;
	}
SpringApplicationRunListener
public interface SpringApplicationRunListener {
//第一次调用run时调用,早期初始化
   default void starting() {
   }
//在环境准备好后调用,但在ApplicationContext创建之前调用
   default void environmentPrepared(ConfigurableEnvironment environment) {
   }
//ApplicationContext之后,加载之前调用
   default void contextPrepared(ConfigurableApplicationContext context) {
   }
//加载之后,刷新之前
   default void contextLoaded(ConfigurableApplicationContext context) {
   }
//上下文已刷新,应用程序已启动,但CommandLineRunners 和 ApplicationRunner尚未调用 
   default void started(ConfigurableApplicationContext context) {
   }
//在运行方法完成之前立即调用,当应用程序上下文具有已刷新所有 CommandLineRunners和ApplicationRunners已被调用
   default void running(ConfigurableApplicationContext context) {
   }
//在运行应用程序时发生故障时调用。
   default void failed(ConfigurableApplicationContext context, Throwable exception) {
   }
prepareEnvironment
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		listeners.environmentPrepared(environment);
//环境与应用绑定
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
创建上下文
//这里根据不同的环境创建不同的IOC容器,但是创建的都是基于Annotation的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);
					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);
	}

初始化IOC
  1. 将创建好的Environment设置到IOC容器中
  2. 执行IOC容器的后置处理
  3. 执行Initializer
  4. 执行listeners#contextPrepared回调方法
  5. 创建两个组件用来在控制台打印Banner
  6. 加载和注册主启动类
  7. 回调listeners#contextLoaded方法
//初始化IOC
	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将环境配置到上下文中
		context.setEnvironment(environment);
//后置处理
		postProcessApplicationContext(context);
		applyInitializers(context);
		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);
		}
		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);
	}

 后置处理

  1. 如果beanNameGenerator不为null,则将其注册为单例Bean。这意味着在应用程序上下文中只有一个beanNameGenerator实例。
  2. 如果resourceLoader不为null,则根据context的类型设置资源加载器。如果contextGenericApplicationContext类型,则直接设置其资源加载器;如果contextDefaultResourceLoader类型,则设置其类加载器。
  3. 如果addConversionService为true,则将ApplicationConversionService的共享实例设置为应用程序上下文的转换服务。这样可以方便地在应用程序中使用不同类型的数据转换。
	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		if (this.addConversionService) {
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}

 应用初始化器

  1. 使用getInitializers()方法获取所有的初始化器,并遍历它们。
  2. 对于每个初始化器,使用GenericTypeResolver.resolveTypeArgument()方法解析出所需的类型(requiredType)。
  3. 使用Assert.isInstanceOf()方法检查context是否是所需类型的实例。如果不是,将抛出异常并显示错误信息:"Unable to call initializer."。
  4. 如果context是所需类型的实例,则调用初始化器的initialize()方法,并将context作为参数传递。
	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}
刷新上下文
	private void refreshContext(ConfigurableApplicationContext context) {
		refresh((ApplicationContext) context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
后置刷新(预留方法)
	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}

一些其他的相关方法

//配置awt java可视化组件
	private void configureHeadlessProperty() {
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
				System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}

	private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
		if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
			Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
			System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
		}
	}

参考博客

springboot源码分析——启动流程分析_springboot启动流程源码分析-CSDN博客

springboot启动源码解析-纯干货扒源码_springboot 启动源码-CSDN博客

Spring Boot:最全SpringBoot启动流程原理分析(全网最全最完善)-腾讯云开发者社区-腾讯云

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

低调$(生活)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值