武器数据档案馆

前言我们启动一个springboot项目,最简单的就是配置一个springboot启动类,然后运行即可

代码语言:javascript复制@SpringBootApplication

public class SpringBoot {

public static void main(String[] args) {

SpringApplication.run(SpringBoot.class, args);

}

}通过上面的代码,我们可以看出springboot启动的关键主要有两个地方,

第一个就是@SpringBootApplication注解第二个就是 SpringApplication.run(SpringBoot.class, args);SpringBoot启动流程图图1:

图2:

一、@SpringBootApplication 注解解析1.1 @SpringBootApplication我们直接追踪@SpringBootApplication的源码,可以看到其实@SpringBootApplication是一个组合注解,他分别是由底下这些注解组成。

代码语言:javascript复制@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })这些注解虽然看起来很多,但是除去元注解,真正起作用的注解只有以下三个注解:

代码语言:javascript复制@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan那这三个注解是有啥用?其实在Spring Boot 1.2版之前,或者我们初学者刚开始接触springboot时,都还没开始使用@SpringBootApplication这个注解,而是使用以上三个注解启动项目。如果有兴趣的,也可以手动敲敲代码,就会发现这样也可以正常启动项目!

代码语言:javascript复制@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan

public class SpringBoot {

public static void main(String[] args) {

SpringApplication.run(SpringBoot.class, args);

}

}所以说这三个注解才是背后的大佬,@SpringBootApplication只是个空壳子。接下来,我来说明下这三个注解各自的作用。

1.2 @SpringBootConfiguration同样,我们跟踪下@SpringBootConfiguration的源代码,看下他由哪些注解组成

代码语言:javascript复制@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Configuration可以看到,除去元注解,剩下的@Configuration注解我相信大家应该都很熟了!我们springboot为什么可以去除xml配置,靠的就是@Configuration这个注解。所以,它的作用就是将当前类申明为配置类,同时还可以使用@bean注解将类以方法的形式实例化到spring容器,而方法名就是实例名,看下代码你就懂了!

代码语言:javascript复制@Configuration

public class TokenAutoConfiguration {

@Bean

public TokenService tokenService() {

return new TokenService();

}

}作用等同于xml配置文件的

代码语言:javascript复制

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd

http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">

1.3 @ComponentScan我们先说下@ComponentScan作用。他的作用就是扫描当前包以及子包,将有@Component,@Controller,@Service,@Repository等注解的类注册到容器中,以便调用。

注:大家第一眼见到@ComponentScan这个注解的时候是否有点眼熟?之前,一些传统框架用xml配置文件配置的时候,一般都会使用context:component-scan来扫描包。以下两中写法的效果是相同的`

代码语言:javascript复制@Configuration

@ComponentScan(basePackages="XXX")

public class SpringBoot {

}代码语言:javascript复制

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd

http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">

注:如果@ComponentScan不指定basePackages,那么默认扫描当前包以及其子包,而@SpringBootApplication里的@ComponentScan就是默认扫描,所以我们一般都是把springboot启动类放在最外层,以便扫描所有的类。

1.4 @EnableAutoConfiguration这里先总结下@EnableAutoConfiguration的工作原理,大家后面看的应该会更清晰:

它主要就是通过内部的方法,扫描classpath的META-INF/spring.factories配置文件(key-value),将其中的

org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项实例化并且注册到spring容器。

ok,我们同样打开@EnableAutoConfiguration源码,可以发现他是由以下几个注解组成的

代码语言:javascript复制@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(AutoConfigurationImportSelector.class)除去元注解,主要注解就是@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)

我们springboot项目为什么可以自动载入应用程序所需的bean?就是因为这个神奇的注解@Import。那么这个@Import怎么这么牛皮?没关系!我们一步一步的看下去!

首先我们先进入AutoConfigurationImportSelector类,可以看到他有一个方法selectImports()

继续跟踪,进入getAutoConfigurationEntry()方法,可以看到这里有个List集合,那这个List集合又是干嘛的?没事,我们继续跟踪getCandidateConfigurations()方法!

可以看到这里有个方法,这个方法的作用就是读取classpath下的META-INF/spring.factories文件的配置,将key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项读取出来,通过反射机制实例化为配置文件,然后注入spring容器。

注:假如你想要实例化一堆bean,可以通过配置文件先将这些bean实例化到容器,等其他项目调用时,在spring.factories中写入这个配置文件的路径即可!

然后直接在SpringFactoriesLoader.loadFactoryNames;这个方法后面打个断点,可以在返回的集合里找到我们自定义的配置文件路径!

说明成功引入我们自定义的依赖包!

二、SpringApplication.run()原理解析SpringApplication.run()原理相对于前面注解的原理,会稍微麻烦点,为了方便我会适当贴出一些注解代码。

首先我们点击查看run方法的源码

可以看出,其实SpringApplication.run()包括两个部分,一部分就是创建SpringApplicaiton实例,另一部分就是调用run()方法,那他们又是怎么运行的?

2.1 SpringApplicaiton创建继续跟踪SpringApplication实例的源码

继续跟踪进入,到如下这个方法中

代码语言:javascript复制@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));

//获取所有监听器

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

//定位main方法

this.mainApplicationClass = deduceMainApplicationClass();

}2.1.1 获取应用类型跟踪deduceFromClasspath方法

从返回结果我们可以看出应用类型一共有三种,分别是

NONE: 非web应用,即不会启动服务器SERVLET: 基于servlet的web应用REACTIVE: 响应式web应用(暂未接触过)判断一共涉及四个常量:

WEBFLUX_INDICATOR_CLASS , WEBMVC_INDICATOR_CLASS,JERSEY_INDICATOR_CLASS,SERVLET_INDICATOR_CLASSES

springboot在初始化容器的时候,会对以上四个常量所对应的class进行判断,看看他们是否存在,从而返回应用类型!至于常量代表哪些class,大家可以自己跟踪看看,也在当前类中

2.1.2 获取初始化器跟踪进入getSpringFactoriesInstances方法

代码语言:javascript复制 private Collection getSpringFactoriesInstances(Class type) {

return getSpringFactoriesInstances(type, new Class[] {});

}

private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {

ClassLoader classLoader = getClassLoader();

// Use names and ensure unique to protect against duplicates

//获取所有初始化器的名称集合

Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

//根据名称集合实例化这些初始化器

List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

//排序

AnnotationAwareOrderComparator.sort(instances);

return instances;

}从代码可以看出是在META-INF/spring.factories配置文件里获取初始化器,然后实例化、排序后再设置到initializers属性中

2.1.3 获取初监听器其实监听器和初始化的操作是基本一样的

2.1.4 定位main方法跟踪源码进入deduceMainApplicationClass方法

代码语言:javascript复制private Class deduceMainApplicationClass() {

try {

//通过创建运行时异常的方式获取栈

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;

}2.2 SpringApplication.run()调用2.2.1 Run代码总览代码语言:javascript复制public ConfigurableApplicationContext run(String... args) {

//1、创建并启动计时监控类

StopWatch stopWatch = new StopWatch();

stopWatch.start();

//2、初始化应用上下文和异常报告集合

ConfigurableApplicationContext context = null;

Collection exceptionReporters = new ArrayList<>();

//3、设置系统属性“java.awt.headless”的值,默认为true,用于运行headless服务器,进行简单的图像处理,多用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true

configureHeadlessProperty();

//4、创建所有spring运行监听器并发布应用启动事件,简单说的话就是获取SpringApplicationRunListener类型的实例(EventPublishingRunListener对象),并封装进SpringApplicationRunListeners对象,然后返回这个SpringApplicationRunListeners对象。说的再简单点,getRunListeners就是准备好了运行时监听器EventPublishingRunListener。

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting();

try {

//5、初始化默认应用参数类

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

//6、根据运行监听器和应用参数来准备spring环境

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

//将要忽略的bean的参数打开

configureIgnoreBeanInfo(environment);

//7、创建banner打印类

Banner printedBanner = printBanner(environment);

//8、创建应用上下文,可以理解为创建一个容器

context = createApplicationContext();

//9、准备异常报告器,用来支持报告关于启动的错误

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,

new Class[] { ConfigurableApplicationContext.class }, context);

//10、准备应用上下文,该步骤包含一个非常关键的操作,将启动类注入容器,为后续开启自动化提供基础

prepareContext(context, environment, listeners, applicationArguments, printedBanner);

//11、刷新应用上下文

refreshContext(context);

//12、应用上下文刷新后置处理,做一些扩展功能

afterRefresh(context, applicationArguments);

//13、停止计时监控类

stopWatch.stop();

//14、输出日志记录执行主类名、时间信息

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);

}

//15、发布应用上下文启动监听事件

listeners.started(context);

//16、执行所有的Runner运行器

callRunners(context, applicationArguments);

}catch (Throwable ex) {

handleRunFailure(context, ex, exceptionReporters, listeners);

throw new IllegalStateException(ex);

}

try {

//17、发布应用上下文就绪事件

listeners.running(context);

}catch (Throwable ex) {

handleRunFailure(context, ex, exceptionReporters, null);

throw new IllegalStateException(ex);

}

//18、返回应用上下文

return context;

}2.2.2 创建spring运行监听器并发布应用启动事件代码语言:javascript复制SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting();

//创建spring监听器

private SpringApplicationRunListeners getRunListeners(String[] args) {

Class[] types = new Class[] { SpringApplication.class, String[].class };

return new SpringApplicationRunListeners(logger,

getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));

}

SpringApplicationRunListeners(Log log, Collection listeners) {

this.log = log;

this.listeners = new ArrayList<>(listeners);

}

//循环遍历获取监听器

void starting() {

for (SpringApplicationRunListener listener : this.listeners) {

listener.starting();

}

}

//此处的监听器可以看出是事件发布监听器,主要用来发布启动事件

@Override

public void starting() {

//这里是创建application事件‘applicationStartingEvent’

this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));

}

//applicationStartingEvent是springboot框架最早执行的监听器,在该监听器执行started方法时,会继续发布事件,主要是基于spring的事件机制

@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);

}

}

}SpringApplicationRunListener这个接口是干啥的呢?没错,他就是用来加载我们配置文件用的。接下来我弄个简单的例子,大家就知道怎么用了。

2.2.2 番外(listener示例,可跳过)主要实现自定义监听器并且读取我们配置文件内容,先献上我的文件结构

创建一个maven项目,pom配置只需要添加web依赖即可

代码语言:javascript复制

org.springframework.boot

spring-boot-starter-parent

2.1.8.RELEASE

org.springframework.boot

spring-boot-starter-web

在resource自定义配置文件my.properties

代码语言:javascript复制tzr.name=zzk自定义监听器,这里主要是对starting、environmentPrepared、started、running方法进行实现

代码语言:javascript复制package zzk;

import java.io.IOException;

import java.util.Properties;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.SpringApplicationRunListener;

import org.springframework.context.ConfigurableApplicationContext;

import org.springframework.core.Ordered;

import org.springframework.core.env.ConfigurableEnvironment;

import org.springframework.core.env.MutablePropertySources;

import org.springframework.core.env.PropertiesPropertySource;

import org.springframework.core.env.PropertySource;

/**

* 集成监听器加载我们的配置文件

* @author zhanzhk

*

*/

public class MyListener implements SpringApplicationRunListener,Ordered {

private SpringApplication application;

private String[] args;

@Override

public void starting() {

System.out.println("表示准备开始使用监听器");

}

public MyListener(SpringApplication application, String[] args) {

this.application = application;

this.args = args;

}

@Override

public void environmentPrepared(ConfigurableEnvironment environment) {

System.out.println("表示已经开始读取配置文件");

//配置文件到程序,再然后放入springboot容器

Properties properties=new Properties();

try {

//读取properties容器

properties.load(this.getClass().getClassLoader().getResourceAsStream("my.properties"));

//读取名字为my

PropertySource propertySource=new PropertiesPropertySource("my",properties) ;

//加载资源到springboot容器

MutablePropertySources propertySources=environment.getPropertySources();

propertySources.addLast(propertySource);

//换种思路,如果你配置文件是放在网络上,可以直接读取放入我们的项目中

} catch (IOException e) {

System.out.println("出错");

}

}

@Override

public void contextPrepared(ConfigurableApplicationContext context) {

// TODO Auto-generated method stub

}

@Override

public void contextLoaded(ConfigurableApplicationContext context) {

// TODO Auto-generated method stub

}

@Override

public void started(ConfigurableApplicationContext context) {

System.out.println("表示初始化容器已经结束");

}

@Override

public void running(ConfigurableApplicationContext context) {

System.out.println("表示可以使用springboot了");

}

@Override

public void failed(ConfigurableApplicationContext context, Throwable exception) {

// TODO Auto-generated method stub

}

//读取优先级

@Override

public int getOrder() {

// TODO Auto-generated method stub

return -1;

}

}然后编写controller文件对我们的配置参数进行调用

代码语言:javascript复制package zzk.controller;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

@Controller

public class Application {

@Value("${tzr.name}")

private String name;

@RequestMapping("test")

@ResponseBody

public String test() {

String x = name;

return x;

}

}ok,那我们自定义的监听器springboot程序又是如何获取的?前面我们代码里讲过了,它主要是读取META-INF底下的spring.factories文件,然后获取监听器,ok那就简单了,我们直接照着EventPublishingRunListener一样在resource增加METAA_INF/spring.factories文件

代码语言:javascript复制org.springframework.boot.SpringApplicationRunListener=\

zzk.MyListener最后设置spring启动器

代码语言:javascript复制package zzk;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class Springboot {

public static void main(String[] args) {

SpringApplication.run(Springboot.class, args);

}

}启动!可以看到启动信息

然后我们再调用controller方法

成功读取我们自定义的配置文件,现在再回头看看run方法,是不是就清晰了!

2.2.3 准备基础运行环境初始化默认应用参数类

代码语言:javascript复制ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

public DefaultApplicationArguments(String... args) {

Assert.notNull(args, "Args must not be null");

this.source = new Source(args);

this.args = args;

}根据运行监听器和应用参数来准备spring环境

代码语言:javascript复制ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

//详细环境的准备

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,

ApplicationArguments applicationArguments) {

// 获取或者创建应用环境

ConfigurableEnvironment environment = getOrCreateEnvironment();

// 配置应用环境,配置propertySource和activeProfiles

configureEnvironment(environment, applicationArguments.getSourceArgs());

//listeners环境准备,广播ApplicationEnvironmentPreparedEvent

ConfigurationPropertySources.attach(environment);

listeners.environmentPrepared(environment);

//将环境绑定给当前应用程序

bindToSpringApplication(environment);

//对当前的环境类型进行判断,如果不一致进行转换

if (!this.isCustomEnvironment) {

environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,

deduceEnvironmentClass());

}

//配置propertySource对它自己的递归依赖

ConfigurationPropertySources.attach(environment);

return environment;

}

// 获取或者创建应用环境,根据应用程序的类型可以分为servlet环境、标准环境(特殊的非web环境)和响应式环境

private ConfigurableEnvironment getOrCreateEnvironment() {

//存在则直接返回

if (this.environment != null) {

return this.environment;

}

//根据webApplicationType创建对应的Environment

switch (this.webApplicationType) {

case SERVLET:

return new StandardServletEnvironment();

case REACTIVE:

return new StandardReactiveWebEnvironment();

default:

return new StandardEnvironment();

}

}

//配置应用环境

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {

if (this.addConversionService) {

ConversionService conversionService = ApplicationConversionService.getSharedInstance();

environment.setConversionService((ConfigurableConversionService) conversionService);

}

//配置property sources

configurePropertySources(environment, args);

//配置profiles

configureProfiles(environment, args);

}创建banner的打印类

代码语言:javascript复制Banner printedBanner = printBanner(environment);//打印类的详细操作过程private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { return null; } ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }创建应用的上下文:根据不同哦那个的应用类型初始化不同的上下文应用类

代码语言:javascript复制context = createApplicationContext();

protected ConfigurableApplicationContext createApplicationContext() {

return this.applicationContextFactory.create(this.webApplicationType);

}

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

}2.2.4 准备上下文代码语言:javascript复制prepareContext(context, environment, listeners, applicationArguments, printedBanner);

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {

//应用上下文的environment

context.setEnvironment(environment);

//应用上下文后处理

postProcessApplicationContext(context);

//为上下文应用所有初始化器,执行容器中的

applicationContextInitializer(spring.factories的实例),

//将所有的初始化对象放置到context对象中

applyInitializers(context);

//触发所有SpringApplicationRunListener监听器的ContextPrepared事件方法。添加所有的事件监听器

listeners.contextPrepared(context);

//记录启动日志

if (this.logStartupInfo) {

logStartupInfo(context.getParent() == null);

logStartupProfileInfo(context);

}

// 注册启动参数bean,将容器指定的参数封装成bean,注入容器

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

beanFactory.registerSingleton("springApplicationArguments", applicationArguments);

//设置banner

if (printedBanner != null) {

beanFactory.registerSingleton("springBootBanner", printedBanner);

}

if (beanFactory instanceof DefaultListableBeanFactory) {

((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);

}

if (this.lazyInitialization) {

context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());

}

// 加载所有资源,指的是启动器指定的参数

Set sources = getAllSources();

Assert.notEmpty(sources, "Sources must not be empty");

//将bean加载到上下文中

load(context, sources.toArray(new Object[0]));

//触发所有springapplicationRunListener监听器的contextLoaded事件方法,

listeners.contextLoaded(context);

}

//这里没有做任何的处理过程,因为beanNameGenerator和resourceLoader默认为空,可以方便后续做扩展处理

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());

}

}

//将启动器类加载到spring容器中,为后续的自动化配置奠定基础,之前看到的很多注解也与此相关

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();

}

//springboot会优先选择groovy加载方式,找不到在选择java方式

private int load(Class source) {

if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {

// Any GroovyLoaders added in beans{} DSL can contribute beans here

GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);

load(loader);

}

if (isComponent(source)) {

this.annotatedReader.register(source);

return 1;

}

return 0;

}2.2.5 刷新上下文springboot的启动分为两部分,一部分是注解,一部分是SpringApplication.run(Springboot.class, args),那么我们的注解又是如何嵌入到程序中呢?靠的就是refreshContext方法,同理,我们跟踪源码进入refreshContext方法

代码语言:javascript复制@Override

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// 准备这个上下文来刷新。

prepareRefresh();

// 告诉子类刷新内部bean工厂。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 准备bean在此上下文中使用。

prepareBeanFactory(beanFactory);

try {

// 允许在上下文子类中对bean工厂进行后处理。

postProcessBeanFactory(beanFactory);

// 调用在上下文中注册为bean的工厂处理器。

invokeBeanFactoryPostProcessors(beanFactory);

// 注册拦截bean创建的bean处理器。

registerBeanPostProcessors(beanFactory);

// 初始化此上下文的消息源。

initMessageSource();

// 为此上下文初始化事件多播。

initApplicationEventMulticaster();

// 在特定的上下文子类中初始化其他特殊bean。

onRefresh();

// 检查侦听器bean并注册它们。

registerListeners();

// 实例化所有剩余的(非拉齐-init)单例。

finishBeanFactoryInitialization(beanFactory);

// 最后一步:发布相应事件.

finishRefresh();

}

catch (BeansException ex) {

if (logger.isWarnEnabled()) {

logger.warn("Exception encountered during context initialization - " +

"cancelling refresh attempt: " + ex);

}

// 销毁已经创建的单例以避免悬空资源。

destroyBeans();

// 重置“actiove”标志。

cancelRefresh(ex);

// 向调用者传播异常。

throw ex;

}

finally {

// 重置Spring核心中常见的内省缓存,因为我们可能不再需要单例bean的元数据了。。。

resetCommonCaches();

}

}

}到这里,就可以看到一系列bean的操作,继续跟踪进入invokeBeanFactoryPostProcessors(调用在上下文中注册为bean的工厂处理器)方法

进入ConfigurationClassParser这个类后,方法调用也是挺绕的,这里就不深究了…进入这个类主要是想看下它的一些方法,因为对于springboot注解的引用就是在这个类进行的,比如doProcessConfigurationClass:

代码语言:javascript复制 @Nullable

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)

throws IOException {

if (configClass.getMetadata().isAnnotated(Component.class.getName())) {

// Recursively process any member (nested) classes first

processMemberClasses(configClass, sourceClass);

}

//处理 @PropertySource 注解

for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(

sourceClass.getMetadata(), PropertySources.class,

org.springframework.context.annotation.PropertySource.class)) {

if (this.environment instanceof ConfigurableEnvironment) {

processPropertySource(propertySource);

}

else {

logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +

"]. Reason: Environment must implement ConfigurableEnvironment");

}

}

// 处理 @ComponentScan 注解

Set componentScans = AnnotationConfigUtils.attributesForRepeatable(

sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

if (!componentScans.isEmpty() &&

!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {

for (AnnotationAttributes componentScan : componentScans) {

// The config class is annotated with @ComponentScan -> perform the scan immediately

Set scannedBeanDefinitions =

this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

// Check the set of scanned definitions for any further config classes and parse recursively if needed

for (BeanDefinitionHolder holder : scannedBeanDefinitions) {

BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();

if (bdCand == null) {

bdCand = holder.getBeanDefinition();

}

if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {

parse(bdCand.getBeanClassName(), holder.getBeanName());

}

}

}

}

//处理 @Import 注解

processImports(configClass, sourceClass, getImports(sourceClass), true);

//处理 @ImportResource 注解

AnnotationAttributes importResource =

AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);

if (importResource != null) {

String[] resources = importResource.getStringArray("locations");

Class readerClass = importResource.getClass("reader");

for (String resource : resources) {

String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);

configClass.addImportedResource(resolvedResource, readerClass);

}

}

//处理 @Bean 注解

Set beanMethods = retrieveBeanMethodMetadata(sourceClass);

for (MethodMetadata methodMetadata : beanMethods) {

configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));

}

// Process default methods on interfaces

processInterfaces(configClass, sourceClass);

// Process superclass, if any

if (sourceClass.getMetadata().hasSuperClass()) {

String superclass = sourceClass.getMetadata().getSuperClassName();

if (superclass != null && !superclass.startsWith("java") &&

!this.knownSuperclasses.containsKey(superclass)) {

this.knownSuperclasses.put(superclass, configClass);

// Superclass found, return its annotation metadata and recurse

return sourceClass.getSuperClass();

}

}

// No superclass -> processing is complete

return null;

}2.2.6 刷新上下文后置处理代码语言:javascript复制afterRefresh(context, applicationArguments);

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {

}2.2.7 上下文准备后其他处理停止计时监控类:计时监听器停止,并统计一些任务执行信息

代码语言:javascript复制stopWatch.stop();

public void stop() throws IllegalStateException {

if (this.currentTaskName == null) {

throw new IllegalStateException("Can't stop StopWatch: it's not running");

}

long lastTime = System.nanoTime() - this.startTimeNanos;

this.totalTimeNanos += lastTime;

this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);

if (this.keepTaskList) {

this.taskList.add(this.lastTaskInfo);

}

++this.taskCount;

this.currentTaskName = null;

}输出日志记录执行主类名、时间信息

代码语言:javascript复制if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);

}发布应用上下文启动完成事件:触发所有SpringapplicationRunListener监听器的started事件方法

代码语言:javascript复制listeners.started(context);

void started(ConfigurableApplicationContext context) {

for (SpringApplicationRunListener listener : this.listeners) {

listener.started(context);

}

}执行所有Runner执行器:执行所有applicationRunner和CommandLineRunner两种运行器

代码语言:javascript复制callRunners(context, applicationArguments);

private void callRunners(ApplicationContext context, ApplicationArguments args) {

List 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);

}

}

}发布应用上下文就绪事件:触发所有springapplicationRunnListener的running事件方法

代码语言:javascript复制listeners.running(context);

void running(ConfigurableApplicationContext context) {

for (SpringApplicationRunListener listener : this.listeners) {

listener.running(context);

}

}返回上下文

代码语言:javascript复制return content;2.3 内置Tomcat其实,内置tomcat应该要归在refreshContext讲的,因为tomcat就是在注解引入的类中生成的,而refreshContext可以引入注解。不过为了更清楚,这里分开吧。前面说了,我们refreshContext是刷新上下文,那如果想要知道上下文中是否存在生成tomcat的类,我们直接去最后返回的上下文中找对应的类即可!

ok,我们在main方法写获取上下文的代码,并且打印出对应的name

代码语言:javascript复制package zzk;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication

public class Springboot {

public static void main(String[] args) {

ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Springboot.class, args);

String[] xs = configurableApplicationContext.getBeanDefinitionNames();

for (String x : xs) {

System.out.println(x);

}

}

}直接启动,可以看到有org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration这个类

ok,我们点开这个类,跟踪源码

我们知道,springboot其实有三种内容服务器,分别是Tomcat,Jetty,Undertow。默认内置tomcat,。我们继续跟踪EmbeddedTomcat.class

可以看到,其实这里的tomcat服务器是内部通过java代码实现的。到这里,run()方法就算结束了。如果后续想深入了解的可以自己看看源码。其实,run()方法总结起来并不多,大方向无非是配置环境参数,引入注解刷新上下文。其他的一些捕获异常、计时操作都是非重点操作

总结springboot原理:包装spring核心注解,使用springmvc无xml进行启动,通过自定义starter和maven依赖简化开发代码,开发者能够快速整合第三方框架,通过java语言内嵌入tomcat

springboot启动流程代码语言:javascript复制--------------------------------创建springbootApplication对象---------------------------------------------

1. 创建springbootApplication对象springboot容器初始化操作

2. 获取当前应用的启动类型。

注1:通过判断当前classpath是否加载servlet类,返回servlet web启动方式。

注2:webApplicationType三种类型:

1.reactive:响应式启动(spring5新特性)

2.none:即不嵌入web容器启动(springboot放在外部服务器运行 )

3.servlet:基于web容器进行启动

3. 读取springboot下的META-INFO/spring.factories文件,获取对应的ApplicationContextInitializer装配到集合

4. 读取springboot下的META-INFO/spring.factories文件,获取对应的ApplicationListener装配到集合

5. mainApplicationClass,获取当前运行的主函数

6.

7.

------------------调用springbootApplication对象的run方法,实现启动,返回当前容器的上下文----------------------------------------------

8. 调用run方法启动

9. StopWatch stopWatch = new StopWatch(),记录项目启动时间

10. getRunListeners,读取META-INF/spring.factores,将SpringApplicationRunListeners类型存到集合中

11. listeners.starting();循环调用starting方法

12. prepareEnvironment(listeners, applicationArguments);将配置文件读取到容器中

读取多数据源:classpath:/,classpath:/config/,file:./,file:./config/底下。其中classpath是读取编译后的,file是读取编译前的

支持yml,yaml,xml,properties

13. Banner printedBanner = printBanner(environment);开始打印banner图,就是sprongboot启动最开头的图案

14. 初始化AnnotationConfigServletWebServerApplicationContext对象

15. 刷新上下文,调用注解,refreshContext(context);

16. 创建tomcat

17. 加载springmvc

18. 刷新后的方法,空方法,给用户自定义重写afterRefresh()

19. stopWatch.stop();结束计时

20. 使用广播和回调机制告诉监听者springboot容器已经启动化成功,listeners.started(context);

21. 使用广播和回调机制告诉监听者springboot容器已经启动化成功,listeners.started(context);

22. 返回上下文SpringBoot获取factory文件的方式代码语言:javascript复制private Collection getSpringFactoriesInstances(Class type) {

return getSpringFactoriesInstances(type, new Class[] {});

}

-------------------------------------

private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {

ClassLoader classLoader = getClassLoader();

// Use names and ensure unique to protect against duplicates

Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

AnnotationAwareOrderComparator.sort(instances);

return instances;

}

-----------------------------

public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {

String factoryTypeName = factoryType.getName();

return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());

}

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {

MultiValueMap result = cache.get(classLoader);

if (result != null) {

return result;

}

try {

Enumeration urls = (classLoader != null ?

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

result = new LinkedMultiValueMap<>();

while (urls.hasMoreElements()) {

URL url = urls.nextElement();

UrlResource resource = new UrlResource(url);

Properties properties = PropertiesLoaderUtils.loadProperties(resource);

for (Map.Entry entry : properties.entrySet()) {

String factoryTypeName = ((String) entry.getKey()).trim();

for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {

result.add(factoryTypeName, factoryImplementationName.trim());

}

}

}

cache.put(classLoader, result);

return result;

}

catch (IOException ex) {

throw new IllegalArgumentException("Unable to load factories from location [" +

FACTORIES_RESOURCE_LOCATION + "]", ex);

}

}

-------------------------

private List createSpringFactoriesInstances(Class type, Class[] parameterTypes,

ClassLoader classLoader, Object[] args, Set names) {

List instances = new ArrayList<>(names.size());

for (String name : names) {

try {

//装载class文件到内存

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;

}spring.factory文件中的类的作用代码语言:javascript复制# PropertySource Loaders 属性文件加载器

org.springframework.boot.env.PropertySourceLoader=\

# properties文件加载器

org.springframework.boot.env.PropertiesPropertySourceLoader,\

# yaml文件加载器

org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners 运行时的监听器

org.springframework.boot.SpringApplicationRunListener=\

# 程序运行过程中所有监听通知都是通过此类来进行回调

org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters 错误报告器

org.springframework.boot.SpringBootExceptionReporter=\

org.springframework.boot.diagnostics.FailureAnalyzers

# Application Context Initializers

org.springframework.context.ApplicationContextInitializer=\

# 报告spring容器的一些常见的错误配置

org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\

# 设置spring应用上下文的ID

org.springframework.boot.context.ContextIdApplicationContextInitializer,\

# 使用环境属性context.initializer.classes指定初始化器进行初始化规则

org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\

org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\

# 将内置servlet容器实际使用的监听端口写入到environment环境属性中

org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners

org.springframework.context.ApplicationListener=\

# 应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshEvent

org.springframework.boot.ClearCachesApplicationListener,\

# 监听双亲应用上下文的关闭事件并往自己的孩子应用上下文中传播,相关事件ParentContextAvailableEvent/ContextClosedEvent

org.springframework.boot.builder.ParentContextCloserApplicationListener,\

org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\

# 如果系统文件编码和环境变量中指定的不同则终止应用启动。具体的方法是比较系统属性file.encoding和环境变量spring.mandatory-file-encoding是否相等(大小写不敏感)。

org.springframework.boot.context.FileEncodingApplicationListener,\

# 根据spring.output.ansi.enabled参数配置AnsiOutput

org.springframework.boot.context.config.AnsiOutputApplicationListener,\

# EnvironmentPostProcessor,从常见的那些约定的位置读取配置文件,比如从以下目录读取#application.properties,application.yml等配置文件:

# classpath:

# file:.

# classpath:config

# file:./config/:

# 也可以配置成从其他指定的位置读取配置文件

org.springframework.boot.context.config.ConfigFileApplicationListener,\

# 监听到事件后转发给环境变量context.listener.classes指定的那些事件监听器

org.springframework.boot.context.config.DelegatingApplicationListener,\

# 一个SmartApplicationListener,对环境就绪事件ApplicationEnvironmentPreparedEvent/应用失败事件ApplicationFailedEvent做出响应,往日志DEBUG级别输出TCCL(thread context class loader)的classpath。

org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\

# 检测正在使用的日志系统,默认时logback,,此时日志系统还没有初始化

org.springframework.boot.context.logging.LoggingApplicationListener,\

# 使用一个可以和Spring Boot可执行jar包配合工作的版本替换liquibase ServiceLocator

org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

# Environment Post Processors

org.springframework.boot.env.EnvironmentPostProcessor=\

org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\

org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\

org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\

org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

# Failure Analyzers

org.springframework.boot.diagnostics.FailureAnalyzer=\

org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\

org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer

# FailureAnalysisReporters

org.springframework.boot.diagnostics.FailureAnalysisReporter=\

org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

# Initializers

org.springframework.context.ApplicationContextInitializer=\

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\

org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners

org.springframework.context.ApplicationListener=\

# 另外单独启动一个线程实例化并调用run方法,包括验证器、消息转换器等

org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\

org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\

org.springframework.boot.autoconfigure.condition.OnBeanCondition,\

org.springframework.boot.autoconfigure.condition.OnClassCondition,\

org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\

org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\

org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\

org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\

org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\

org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\

org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\

org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\

org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\

org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\

org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\

org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\

org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\

org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\

org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\

org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\

org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\

org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\

org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\

org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\

org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\

org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\

org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\

org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\

org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\

org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\

org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\

org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\

org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\

org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\

org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\

org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\

org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\

org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\

org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\

org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\

org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\

org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\

org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\

org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\

org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\

org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\

org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\

org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\

org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\

org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\

org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\

org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\

org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\

org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\

org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\

org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\

org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\

org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\

org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\

org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\

org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\

org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\

org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\

org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\

org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\

org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\

org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\

org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\

org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\

org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\

org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\

org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\

org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\

org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\

org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\

org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\

org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\

org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\

org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\

org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\

org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\

org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\

org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\

org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\

org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\

org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\

org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\

org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\

org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\

org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\

org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\

org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\

org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\

org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers

org.springframework.boot.diagnostics.FailureAnalyzer=\

org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\

org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\

org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\

org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\

org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers

org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\

org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\

org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\

org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\

org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\

org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider其他参考:https://blog.csdn.net/weixin_40496191/article/details/109098491

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