这是最常见的也是使用最多的SpringBoot应用程序启动类,整个SpringBoot应用启动都要靠执行run方法来启动
@SpringBootApplication
public class Hibernate52Application {
public static void main(String[] args) {
SpringApplication.run(Hibernate52Application.class, args);
}
}
点进去看看
// 这是SpringApplication的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
// 上面的run方法实际是调用这个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
首先实例化SpringApplication
private ResourceLoader resourceLoader;
private WebApplicationType webApplicationType;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Class<?> mainApplicationClass;
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 资源加载器
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// JavaConfig类型的类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据classPath推导出web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化initializers属性
setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推导出主应用程序类
this.mainApplicationClass = deduceMainApplicationClass();
}
SprigBootApplication 中有非常多的属性,先看这个构造方法中使用到的
- resourceLoader 资源加载器
- primarySources 在这就是 Hibernate52Application.class
- webApplicationType web应用程序类型,看代码是根据Classpath推导而来
- Initializer 元素类型为ApplicationContextInitializer的列表
- listeners 元素类型为ApplicationListener的列表
- mainApplicationClass 主应用程序类
上面的方法,先一个一个看
- deduceFormClasspath
public enum WebApplicationType {
/**
* The application should not run as a web application and should not start an
* embedded web server.
* 没有内嵌服务器
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
* Servlet类型的应用
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
* 响应式的应用 比如Spring-Webflux
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
// 这个方法的作用就是判断当前的应用类型,通过使用反射在类路径下寻找是否存在对应的类,从而判断出该应用属于什么类型的应用
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
}
- getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 加载指定类型在"META-INF/spring.factories"对应的类名数组
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据类名数组创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序实例
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 其中的loadFactoryNames方法并没有点进去看,其实里面的逻辑就是根据type加载在"META-INF/spring.factories"对应的类名数组
// 在META-INF/spring.factories中,以KEY-VALUE形式存储了各个类对应的实现类们
@SuppressWarnings("unchecked")
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 {
// 获取name对应的类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 判断实例是否实现自type类
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;
}
- deduceMainApplicationClass
private Class<?> deduceMainApplicationClass() {
try {
// 获得堆栈元素数组
// 这有意思的是通过new一个运行时异常来获得堆栈元素数组来找到main方法的栈帧
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 判断到底是哪个类执行了main方法
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
// 这个方法的作用主要是用来获得启动类,用来打印启动时的日志,说明应用是通过这个类启动的。
构造方法看完了,现在再看看run方法
public ConfigurableApplicationContext run(String... args) {
// 统计启动时长
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless属性 和awt相关
configureHeadlessProperty();
// 1. 创建SpirngApplicationRunListeners数组
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听
listeners.starting();
try {
// 创建applicationArguments 应用程序参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2. 加载属性配置
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 启动打印banner
Banner printedBanner = printBanner(environment);
// 3. 创建Spring容器
context = createApplicationContext();
// 获得异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class<?>[] { ConfigurableApplicationContext.class }, context);
// 4.准备环境
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 5.刷新环境 (初始化容器)
refreshContext(context);
// 后置刷新 逻辑为空
afterRefresh(context, applicationArguments);
// 停止统计时长
stopWatch.stop();
// 打印启动的时长的日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 启动Spring容器
listeners.started(context);
// 6.调用runners
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;
}
run方法的逻辑大致如此,这里面有几个比较重要的方法还是点进去看一下,分别是getRunListeners,prepareEnvironment,prepareContext,refreshContext,callRunners,
1. getRunListeners
// 该方法获取了SpringApplicationRunListener在META-INF/spring.factories中对应的实现类并且返回了他们的实例
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
//这里实际上也是用了上面的getSpringFactoriesInstances方法
此时的Listeners变量

2. prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 2.1创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2.2配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 2.3属性绑定环境
ConfigurationPropertySources.attach(environment);
// 2.4通知SpringApplicationRunListeners,环境已经准备好
listeners.environmentPrepared(environment);
// 2.5将DefaultProperties移动到environment的最后
DefaultPropertiesPropertySource.moveToEnd(environment);
// 2.6配置其他额外的profiles
configureAdditionalProfiles(environment);
// 2.7把环境绑定到SpringApplication
bindToSpringApplication(environment);
// 2.8如果不是自定义环境,根据条件转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 如果有attach到environment的MutablePropertySources,就添加到environment的PropertySources中
ConfigurationPropertySources.attach(environment);
return environment;
}
2.1 getOrCreateEnvironment
// 创建环境
private ConfigurableEnvironment getOrCreateEnvironment() {
// 存在就返回
if (this.environment != null) {
return this.environment;
}
// 不存在就根据构造方法中推断出的web应用类型来进行创建
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
2.2 configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 设置environment的conversionService属性
if (this.addConversionService) { //true
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 2.2.1配置environment的propertySources属性
configurePropertySources(environment, args);
// 2.2.2配置environment的profiles属性
configureProfiles(environment, args);
}
这涉及到了很多名词比如environment,profile,propertySource等,先挖个坑,知道大体意思就好,具体的细节以后再看
2.2.1 configurePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
if (this.addCommandLineProperties && args.length > 0) {
// 如果存在就替换
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
// 不存在就添加
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
这段代码大体意思就是propertySource可以通过命令行参数进行附加
2.2.2 configureProfiles
// 逻辑为空
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
}
2.3 attach
// 这个方法的作用大概就是往environment的MutablePropertySources中添加一个新的配置
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
2.4 environmentPrepared
//通知SpringApplicationRunListener,环境已经准备完成
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
2.5 moveToEnd
public static final String NAME = "defaultProperties";
// 将defaultProperties移动到environment的PropertySources的最后,至于这么做的目的,不太清楚
public static void moveToEnd(ConfigurableEnvironment environment) {
moveToEnd(environment.getPropertySources());
}
public static void moveToEnd(MutablePropertySources propertySources) {
PropertySource<?> propertySource = propertySources.remove(NAME);
if (propertySource != null) {
propertySources.addLast(propertySource);
}
}
2.6 configureAdditionalProfiles
// 配置额外的配置文件
private void configureAdditionalProfiles(ConfigurableEnvironment environment) {
if (!CollectionUtils.isEmpty(this.additionalProfiles)) {
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles()));
if (!profiles.containsAll(this.additionalProfiles)) {
profiles.addAll(this.additionalProfiles);
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
}
}
2.7 bindToSpringApplication
// 绑定springApplication到environment 不知道有啥用
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
2.8 如果不是自定义条件 则转换
默认情况下 isCustomEnvironment 为false,所以这的代码一般就会执行,不过返回的还是StandardEnvironment类型的environment 代码不放了
3.createApplicationContext
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
// 根据webApplicationType创建spring容器
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
//根据webApplicationType创建不同的context
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
4.prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 上下文设置环境
context.setEnvironment(environment);
// 1.后置处理上下文
postProcessApplicationContext(context);
// 2.初始化Initilizers
applyInitializers(context);
// 3.通知listeners上下文已经准备好
listeners.contextPrepared(context);
// 打印日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 设置beanFactory的属性
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
// 加载BeanDefinition们
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 4.加载
load(context, sources.toArray(new Object[0]));
// 通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。
listeners.contextLoaded(context);
}
4.1 postProcessApplicationContext
//后置处理applicationContext
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());
}
}
// 只在SpringMVC环境下,前两个都为空,也就是只会进入这个分支,context的工厂会set一个conversionService
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
4.2 applyInitializers
// 大概意思就是遍历之后判断非空然后初始化initializer
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);
}
}
4.3 contextPrepared
// 通知SpringApplicationRunListeners:容器已经准备好了
void contextPrepared(ConfigurableApplicationContext context) {
doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
doWithListeners(stepName, listenerAction, StartupStep::end);
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
stepAction.accept(step);
step.end();
}
4.4 load
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
void load() {
for (Object source : this.sources) {
// BeanDefinitionLoader根据sources加载beandefinition
load(source);
}
}
关于IOC加载Bean,可以看【死磕 Spring】—– IOC 之 加载 Bean
5.refreshcontext
private void refreshContext(ConfigurableApplicationContext context) {
// 1.刷新容器
// 这里可以触发springboot的自动配置功能,挖个坑 以后研究
refresh((ApplicationContext) context);
// 2.注册关闭挂钩,主要用来在容器关闭时销毁bean信息
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
6.callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// runners列表
List<Object> runners = new ArrayList<>();
// 获得所有ApplicationRunner实例
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 获得所有CommandLineRunner实例
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序runners
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);
}
}
}
总结
- Spring Boot的启动分为实例化SpringApplication以及run方法。
实例化方法主要做两件事:
1.初始化Initializers
2.初始化Listeners
这两种初始化方法都是通过加载’META-INF/spring.factories’中配置的实现类来实现的。 - Spring Boot的启动方法代码就到此结束了,只是简单的过了一遍,还有好多概念不明白,也有几个坑要填,比如environment,profile到底是啥,还有Spring Boot的自动配置功能到底怎么实现的,以及tomcat是怎么嵌入Spring Boot中的。
本文深入探讨了SpringBoot 2.3.x的启动过程,从run方法开始,逐步解析getRunListeners、prepareEnvironment、prepareContext、refreshContext和callRunners等关键步骤,涉及环境配置、监听器、ApplicationContext初始化和Bean加载等内容。
493

被折叠的 条评论
为什么被折叠?



