前言

        最近在学习Spring Boot相关的课程,过程中以笔记的形式记录下来,方便以后回忆,同时也在这里和大家探讨探讨,文章中有漏的或者有补充的、错误的都希望大家能够及时提出来,本人在此先谢谢了!

开始之前呢,希望大家带着几个问题去学习:

1、Spring Boot SpringApplication 是什么?

2、整体流程或结构是怎样的?

3、重点内容或者核心部分是什么?

4、怎么实现的?

5、是怎么和 Spring 关联起来的?

这是对自我的提问,我认为带着问题去学习,是一种更好的学习方式,有利于加深理解。好了,接下来进入主题。

1、起源

        上篇文章我们讲了 SpringApplication 的准备阶段,在这个阶段,完成了运行时所需要准备的资源,如:initializers、listeners等。而这篇文章我们就来讲讲 SpringApplication 的运行阶段,在这个阶段,它是如何启动 Spring 应用上下文的,且如何与 Spring 事件结合起来,形成完整的 SpringApplication 生命周期的。

注:本篇文章所用到的 Spring Boot版本是 2.1.6.BUILD-SNAPSHOT

2、SpringApplication 运行阶段

        上篇文章我们讲了 SpringApplication 的构造方法,这里我们就来讲讲 SpringApplication 的核心,也就是run方法,代码如下:

public class SpringApplication {

    ...

    public ConfigurableApplicationContext run(String... args) {
// 这是 Spring 的一个计时器,计算代码的执行时间(ms级别)
StopWatch stopWatch = new StopWatch();
stopWatch.start(); // 这俩变量在后面赋值处进行说明
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // 用来设置java.awt.headless属性值
configureHeadlessProperty(); // 该对象属于组合模式的实现,核心是内部关联的 SpringApplicationRunListener 集合,SpringApplicationRunListener 是 Spring Boot 的运行时监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 会在不同的阶段调用对应的方法,这里表示启动run方法被调用
listeners.starting(); try { // 用来获取 SpringApplication.run(args)传入的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 获取 properties 配置文件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 设置 spring.beaninfo.ignore 的属性值,判断是否跳过搜索BeanInfo类
configureIgnoreBeanInfo(environment); // 这里是项目启动时,控制台打印的 Banner
Banner printedBanner = printBanner(environment); // 这里就是创建 Spring 应用上下文
context = createApplicationContext(); // 获取 spring.factories 中key为 SpringBootExceptionReporter 的类名集合
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context); // 这里是准备 Spring 应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 这里是启动 Spring 应用上下文,底层调用的是 ApplicationContext 的 refresh() 方法,到这里就正式进入了 Spring 的生命周期,同时,SpringBoot的自动装配特性也随之启动
refreshContext(context); // 里面是空的,猜测应该是交由开发人员自行扩展
afterRefresh(context, applicationArguments);
stopWatch.stop(); // 这里打印启动信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
} // ApplicationContext 启动时,调用该方法
listeners.started(context); // 项目启动后,做的一些操作,开发人员可自行扩展
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try { // ApplicationContext 启动完成时,调用该方法
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
} ...
}

上面就是整个过程的概览,可以看到,在运行阶段执行的操作比较多,虽然看起来杂乱无章,但其实还是有规律可循的。比如,执行的 SpringApplicationRunListeners 中的阶段方法,刚启动阶段的 starting 、已启动阶段的 started 、启动完成阶段的 running 等。还有对应的 Spring 应用上下文的创建、准备、启动操作等。接下来,就对里面的几个核心对象进行讨论。

2.1 SpringApplicationRunListeners 结构

我们先来看看 SpringApplicationRunListeners 对象,从代码可以看出该对象是由 getRunListeners 方法创建的:

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

可以看到,通过传入的 getSpringFactoriesInstances 方法的返回值,执行 SpringApplicationRunListeners 的构造方法,进行对象的创建。接着看 getSpringFactoriesInstances 方法:

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<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

看到这大家应该比较熟悉了,通过前面几篇文章的讨论我们知道,该方法通过 SpringFactoriesLoader.loadFactoryNames 返回所有 classpass 下的 spring.factories 文件中 key 为 SpringApplicationRunListener 的实现类集合。如 Spring Boot 的内建实现:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

最后,就是将该集合传入 SpringApplicationRunListeners 的构造方法:

class SpringApplicationRunListeners {

    ...

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
} public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
} ... }

里面是将集合赋值到 listeners 属性,可以看到 SpringApplicationRunListeners 属于组合模式的实现,核心其实是内部关联的 SpringApplicationRunListener 对象集合,当外部调用该阶段方法时,就会迭代执行集合中 SpringApplicationRunListener 对应的方法。所以接下来我们就来讨论 SpringApplicationRunListener。

2.1.1 SpringApplicationRunListener 事件和监听机制

SpringApplicationRunListener 负责在 SpringBoot 的不同阶段广播相应的事件,然后调用实际的 ApplicationListener 类,在该类的 onApplicationEvent 方法中,根据不同的 Spring Boot 事件执行相应操作。整个过程大概如此,接下来进行详细讨论,先来看看 SpringApplicationRunListener 定义:

public interface SpringApplicationRunListener {

    // 在run()方法开始执行时被调用,表示应用刚刚启动,对应的 Spring Boot 事件为 ApplicationStartingEvent
void starting(); // ConfigurableEnvironment 构建完成时调用,对应的 Spring Boot 事件为 ApplicationEnvironmentPreparedEvent
void environmentPrepared(ConfigurableEnvironment environment); // ApplicationContext 构建完成时调用,对应的 Spring Boot 事件为 ApplicationContextInitializedEvent
void contextPrepared(ConfigurableApplicationContext context); // ApplicationContext 完成加载但还未启动时调用,对应的 Spring Boot 事件为 ApplicationPreparedEvent
void contextLoaded(ConfigurableApplicationContext context); // ApplicationContext 已启动,但 callRunners 还未执行时调用,对应的 Spring Boot 事件为 ApplicationStartedEvent
void started(ConfigurableApplicationContext context); // ApplicationContext 启动完毕被调用,对应的 Spring Boot 事件为 ApplicationReadyEvent
void running(ConfigurableApplicationContext context); // 应用出错时被调用,对应的 Spring Boot 事件为 ApplicationFailedEvent
void failed(ConfigurableApplicationContext context, Throwable exception); }

我们来看看它的实现类,也就是上面加载的 spring.factories 文件中的 EventPublishingRunListener 类,该类也是 Spring Boot 内建的唯一实现类,具体广播事件的操作在该类中进行,代码如下:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
} @Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
} ... }

可以看到,通过构造方法创建 EventPublishingRunListener 实例的过程中,调用了 getListeners 方法,将 SpringApplication 中所有 ApplicationListener 监听器关联到了 initialMulticaster 属性中。没错,这里的 ApplicationListener 监听器就是上篇文章中在 SpringApplication 准备阶段从 spring.factories 文件加载的 key 为 ApplicationListener 的实现类集合,该实现类集合全部重写了 onApplicationEvent 方法。

2.1.2 SimpleApplicationEventMulticaster 广播器

这里又引出了另一个类, 也就是 SimpleApplicationEventMulticaster ,该类是 Spring 的事件广播器,也就是通过它来广播各种事件。接着,当外部迭代的执行到 EventPublishingRunListener 的 starting 方法时,会通过 SimpleApplicationEventMulticaster 的 multicastEvent 方法进行事件的广播,这里广播的是 ApplicationStartingEvent 事件,我们进入 multicastEvent 方法:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    ...

    @Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
} @Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
}

通过 getApplicationListeners 方法,根据事件类型返回从上面关联的 ApplicationListener 集合中筛选出匹配的 ApplicationListener 集合,根据 Spring Boot 版本的不同,在这个阶段获取到的监听器也有可能不同,如 2.1.6.BUILD-SNAPSHOT 版本返回的是:

然后依次遍历这些监听器,同步或异步的调用 invokeListener 方法:

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
} ... private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}

可以看到,最终调用的是 doInvokeListener 方法,在该方法中执行了 ApplicationListener 的 onApplicationEvent 方法,入参为广播的事件对象。我们就拿其中一个的监听器来看看 onApplicationEvent 中的实现,如 BackgroundPreinitializer 类:

public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {

	...

	@Override
public void onApplicationEvent(SpringApplicationEvent event) {
if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
&& event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
performPreinitialization();
}
if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
&& preinitializationStarted.get()) {
try {
preinitializationComplete.await();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
} ...
}

在该方法中,通过 instanceof 判断事件的类型,从而进行相应的操作。该监听器主要的操作是新建一个后台线程去执行那些耗时的初始化工作,包括验证器、消息转换器等。LoggingApplicationListener 监听器则是对 Spring Boot 的日志系统做一些初始化的前置操作。另外两个监听器在该阶段无任何操作。

至此,SpringBoot 事件机制的整体流程大概如此,我们简要回顾一下几个核心组件:

  • SpringApplicationRunListeners:首先,在 run 方法的执行过程中,通过该类在 SpringBoot 不同的阶段调用不同的阶段方法,如在刚启动阶段调用的 starting 方法。

  • SpringApplicationRunListener:而 SpringApplicationRunListeners 属于组合模式的实现,它里面关联了 SpringApplicationRunListener 实现类集合,当外部调用阶段方法时,会迭代执行该集合中的阶段方法。实现类集合是 spring.factories 文件中定义好的类。这里是一个扩展点,详细的后面述说。

  • EventPublishingRunListener:该类是 Spring Boot 内置的 SpringApplicationRunListener 唯一实现类,所以,当外部调用各阶段的方法时,真正执行的是该类中的方法。

  • SimpleApplicationEventMulticaster:在阶段方法中,会通过 Spring 的 SimpleApplicationEventMulticaster 事件广播器,广播各个阶段对应的事件,如这里的 starting 方法广播的事件是 ApplicationStartingEvent 。

  • ApplicationListener:最后 ApplicationListener 的实现类也就是 Spring Boot 监听器会监听到广播的事件,根据不同的事件,进行相应的操作。这里的 Spring Boot 监听器是也是在 spring.factories 中定义好的,这里我们也可自行扩展。

到这里 Spring Boot 事件监听机制差不多就结束了,值得注意的是 Spring Boot 监听器实现的是 Spring 的 ApplicationListener 类,事件类最终继承的也是 Spring 的 ApplicationEvent 类,所以,Spring Boot 的事件和监听机制都基于 Spring 而实现的。

2.2 ApplicationArguments 加载启动参数

        当执行完 listeners.starting 方法后,接着进入构造 ApplicationArguments 阶段:

public class SpringApplication {

    ...

    public ConfigurableApplicationContext run(String... args) {

		...

		SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ...
}
} ...
}

该类是用于简化 Spring Boot 应用启动参数的封装接口,我们启动项目时输入的命令参数会封装在该类中。一种是通过 IDEA 输入的参数,如下:

另一种是 springboot jar包运行时传递的参数:cmd中运行java -jar xxx.jar name=张三 pwa=123 。

然后,可以通过 @Autowired 注入 ApplicationArguments 的方式进行使用:

public class Test {

    @Autowired
private ApplicationArguments applicationArguments; public void getArgs() {
// 获取 args 中的所有 non option 参数
applicationArguments.getNonOptionArgs(); // 获取 args 中所有的 option 参数的 name
applicationArguments.getOptionNames(); // 获取传递给应用程序的原始未处理参数
applicationArguments.getSourceArgs(); // 获取 args 中指定 name 的 option 参数的值
applicationArguments.getOptionValues("nmae"); // 判断从参数中解析的 option 参数是否包含指定名称的选项
applicationArguments.containsOption("name");
}
}

2.3 ConfigurableEnvironment 加载外部化配置

        接着进入构造 ConfigurableEnvironment 的阶段,该类是用来处理我们外部化配置的,如 properties、YAML 等,提供对配置文件的基础操作。当然,它能处理的外部配置可不仅仅如此,详细的在下篇文章讨论,这里我们进行简要了解即可,进入创建该类的 prepareEnvironment 方法:

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

这里通过 getOrCreateEnvironment 方法返回具体的 Environment:

private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}

可以看到,这里通过 webApplicationType 属性来判断当前应用的类型,有 Servlet 、 Reactive 、 非Web 3种类型,该属性也是在上篇文章中 SpringApplication 准备阶段确定的,这里我们通常都是 Servlet 类型,返回的是 StandardServletEnvironment 实例。

之后,还调用了 SpringApplicationRunListeners 的 environmentPrepared 阶段方法,表示 ConfigurableEnvironment 构建完成,同时向 Spring Boot 监听器发布 ApplicationEnvironmentPreparedEvent 事件。监听该事件的监听器有:

2.4 ConfigurableApplicationContext 创建 Spring 应用上下文

        这里通过 createApplicationContext 方法创建 Spring 应用上下文,实际上 Spring 的应用上下文才是驱动 Spring Boot 的核心引擎:

public class SpringApplication {

    ...

    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);
} ...
}

这里也是通过 webApplicationType 属性来确定应用类型从而创建 String 上下文,上篇文章说到该属性值是在 Spring Boot 准备阶段推导出来的。这里我们的应用类型是 Servlet ,所以创建的是 AnnotationConfigServletWebServerApplicationContext 对象。创建完 Spring 应用上下文之后,执行 prepareContext 方法进入准备上下文阶段:

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);
}
// 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. 设置了 Spring 应用上下文的 ApplicationArguments,上面说过是处理外部化配置的,具体类型为 StandardServletEnvironment 。

  2. Spring 应用上下文后置处理,主要是覆盖当前 Spring 应用上下文默认所关联的 ResourceLoader 和 ClassLoader 。

  3. 执行 Spring 的初始化器,上篇文章说过在 Spring Boot 准备阶段初始化了一批在 spring.factories 文件中定义好的 ApplicationContextInitializer ,这里就是执行它们的 initialize 方法,同时这里也是一个扩展点,后面详细讨论。

  4. 执行 SpringApplicationRunListeners 的 contextPrepared 阶段方法,表示 ApplicationContext 准备完成,同时向 Spring Boot 监听器发布 ApplicationContextInitializedEvent 事件 。

  5. 将 springApplicationArguments 和 springBootBanner 注册为 Bean。

  6. 加载 Spring 应用上下文的配置源,也是在上篇文章 Spring Boot 准备阶段获取的 primarySources 和 sources ,primarySources 来源于 SpringApplication 构造器参数,sources 则来源于自定义配置的 setSources 方法。

  7. 最后执行 SpringApplicationRunListeners 的 contextLoaded 阶段方法,表示 ApplicationContext 完成加载但还未启动,同时向 Spring Boot 监听器发布 ApplicationPreparedEvent 事件 。

接下来就是真正启动阶段,执行的是 refreshContext 方法:

private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}

可以看到,底层调用的是 AbstractApplicationContext 的 refresh 方法,到这里 Spring 应用正式启动,Spring Boot 核心特性也随之启动,如自动装配。随后执行 SpringApplicationRunListeners 的 started 阶段方法,表示 ApplicationContext 已启动,同时向 Spring Boot 监听器发布 ApplicationStartedEvent 事件 。但还未启动完成,后面还有一个 callRunners 方法,一般来讲,里面执行一些我们自定义的操作。之后 Spring 应用才算启动完成,随后调用 running 方法,发布 ApplicationReadyEvent 事件。至此,SpringApplication 运行阶段结束。

3、总结

        最后来对 SpringApplication 运行阶段做一个总结。这个阶段核心还是以启动 Spring 应用上下文为主,同时根据应用类型来初始化不同的上下文对象,但这些对象的基类都是 Spring 的 ConfigurableApplicationContext 类。且在启动的各个阶段中,使用 SpringApplicationRunListeners 进行事件广播,回调 Spring Boot 的监听器。同时还初始化了 ApplicationArguments 、ConfigurableEnvironment 等几个组件。下篇文章我们就来讨论 Spring Boot 的外部化配置部分,来看看为什么外部的各个组件,如 Redis、Dubbo 等在 properties 文件中进行相应配置后,就可以正常使用。

以上就是本章的内容,如过文章中有错误或者需要补充的请及时提出,本人感激不尽。

参考:

《Spring Boot 编程思想》

https://www.cnblogs.com/youzhibing/p/9603119.html

https://www.jianshu.com/p/b86a7c8b3442

https://www.cnblogs.com/duanxz/p/11243271.html

https://www.jianshu.com/p/7a674c59d76e

Spring Boot SpringApplication启动类(二)的更多相关文章

  1. Spring Boot SpringApplication启动类(一)

    目录 目录 前言 1.起源 2.SpringApplication 准备阶段 2.1.推断 Web 应用类型 2.2.加载应用上下文初始器 ApplicationContextInitializer ...

  2. Spring Boot 要点--启动类和热部署

    spring boot需要一个启动类 比如 package com.tianmaying; import org.springframework.boot.SpringApplication; imp ...

  3. 【spring boot】启动类启动 错误: 找不到或无法加载主类 com.codingapi.tm.TxManagerApplication 的解决方案

    导入的一个外部的spring boot项目,运行启动类,出现错误:找不到或无法加载主类 com.codingapi.tm.TxManagerApplication 解决方案: 将所有错误处理完成后,再 ...

  4. spring boot无法启动,或者正常启动之后无法访问报404的解决办法

    以前用spring boot都是用idea的自动创建,或者是用的Jhipster创建的,就没有深究怎么去搭建.但是今天晚上心血来潮,想自己搭一个demo来整合一些技术,于是就花一点时间来手动搭.因为今 ...

  5. Spring Boot 自动装配(二)

    目录 目录 前言 1.起源 2.Spring Boot 自动装配实现 2.1.@EnableAutoConfiguration 实现 2.1.1. 获取默认包扫描路径 2.1.2.获取自动装配的组件 ...

  6. 【转载】Spring boot学习记录(二)-配置文件解析

    前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 Spring Boot使用了一个全局的配置文件application.prop ...

  7. spring boot容器启动详解

    目录 一.前言 二.容器启动 三.总结 =======正文分割线====== 一.前言 spring cloud大行其道的当下,如果不了解基本原理那么是很纠结的(看见的都是约定大于配置,但是原理呢?为 ...

  8. 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean

    让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...

  9. 2. Spring Boot项目启动原理初探

    SpringBoot从宏观上说,就是对spring容器进行了一层包装.它内部的入口是利用 SpringApplication类的static的 run 方法进行启动的,调用的图: 上图中的这些方法都位 ...

随机推荐

  1. web中间件常见漏洞总结笔记

    之前看吐司别人发的个文档,简单记的笔记 ----- IIS     解析漏洞        IIS 6            *.asp;.jpg会被当作asp解析            *.asp/ ...

  2. JAVA内存溢出与内存泄露

    虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险. 最近在网上搜集了一些资料,现整理如下: —————————————————————————————————————— ...

  3. 使用Typescript重构axios(二十二)——请求取消功能:收尾

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  4. python学习之【第八篇】:Python中的函数基础

    1.前言 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.函数能提高应用的模块性,和代码的重复利用率. 2.函数的定义 定义函数时需要遵守以下规则: 函数代码块以 def 关键词开头 ...

  5. 关于一个 websocket 多节点分布式问题的头条面试题

    原文链接,欢迎讨论: [Q023]websocket 服务多节点部署时会有什么问题,怎么解决 你来说说 websocket 有什么用 双向通信,服务器端可以主动 push,给客户端发送通知 那webs ...

  6. html5自动横屏的方法

    html5自动横屏的方法<pre>var evt = "onorientationchange" in window ? "orientationchange ...

  7. on duplicate key update 的使用(数据库有就修改,没有就添加数据)

    on duplicate key update 使用:当数据库中有该数据就修改,没有就添加 MySQL语句如下: # id 不存在则添加数据,id存在就更新数据 INSERT INTO t_user( ...

  8. python脚本 环境准备

    现在的公司用 Python 做 Web 开发,入职到现在为止(三个月),算是入门了 Python Web 开发 但是 Python 本身是脚本语言,我还从来没有体会到脚本语言能给日常工作带来的便利 就 ...

  9. google在nature上发表的关于量子计算机的论文(Quantum supremacy using a programmable superconducting processor 译)— 附论文

    Google 2019年10月23号发表在Nature(<自然><科学>及<细胞>杂志都是国际顶级期刊,貌似在上面发文两篇,就可以评院士了)上,关于量子计算(基于 ...

  10. c#属性1(Property)

    创建一个只读属性 using System; using System.Collections; using System.Collections.Generic; using System.Glob ...