Spring Boot简介
Spring boot的资料获取
https://spring.io/projects/spring-boot 官方网站
https://github.com/spring-projects/spring-boot github源码地址
https://stackoverflow.com/questions/tagged/spring-boot stackoverflow问题汇总
SpringBoot版本选择
以2.3.5 CURRENT AG这个当前版本为例:
2. 主版本,主版本一般情况下是不变的,除非有大机制或者是架构的调整,才会去调整主版本。且主版本之间一般是不相兼容的。
3. 次版本,此版本主要是在主版本架构不管的情况下,增加一些新特性。可以理解为发布的新特性。同一个主版本下的次要版本之间通常是要保证兼容的。
5. 增量版本,bug修复。
最后一个“单词/缩略形式”版本大概有如下,这些定义由开发团队商量来定义,用来描述一个版本的发布状态(发布计划):
GA:General Available:通用可使用,官方推荐使用此版本
PRE:预览版,内测版
SNAPSHOT
RC:Release
稳定性(由小到大):
PRE<SNAPSHOT<RC<GA
一些有关Spring Boot的问题.
Spring Boot 是如何基于Spring Framework逐步走向自动化装配的
SpringApplication 是怎样掌控Spring应用的生命周期的
Spring Boot 外部化配置与Spring Environment 抽象之间是什么关系?
Spring Web MVC 向Spring Reactive WebFlux过渡的真实价值和意义?
课程收获
Spring 全栈技术和实现原理
Spring Boot 核心技术
BAT 大规模微服务基础设施开发与生产实施经验
为什么说Spring Boot 易学难精
组件自动装配: 规约大于配置,专注核心业务
(难精)模式注解,@Eable模块,条件装配,加载机制
外部化配置:一次构建,按需调配,到处运行
(难精)Enviroment抽象,生命周期,破坏性变更
嵌入式容器: 内置容器,无需部署,独立运行
(难精)Servlet web容器,Reactive Web容器
Spring Boot Stater: 简化依赖,按需装配,自我包含
(难精)依赖管理,装配条件,装配顺序.
Production-Ready:一站式运维,生态无缝整合
(难精)健康检查,数据指标,@Endpoint管控
Spring Boot 与Java EE规范
Web: Servlet (JSR-315,JSR-340)
SQL:JDBC(JSR-221)
数据校验:Bean Validation(JSR-303,JSR-349)
缓存:Java Caching API(JSR-107)
WebSockets: Java API for WebSocket
理解SpringApplication
SpringApplication的准备阶段
定义:Spring应用引导类,提供便利的自定义行为方法
配置:Spring Bean来源
推断:Web 应用类型和主引导类(Main Class)
加载:应用上下文初始器 和应用事件监听器
场景:嵌入式Web应用和非Web应用
SpringApplication的运行阶段
基础技术
Spring Framework
1.Spring模式注解
2.Spring应用上下文
3.Spring工厂加载机制
4.Spring应用上下文初始器
5.Spring Environment抽象
6.Spring 应用事件/监听器
衍生技术
SpringApplication
SpringApplication Builder API
SpringApplicaiton 运行监听器
SpringApplication 参数
SpringApplication 故障分析
Spring Boot应用事件/监听
主要类的源码分析
SpringApplication准备阶段
/**
* Class that can be used to bootstrap and launch a Spring application from a Java main
* method. By default class will perform the following steps to bootstrap your
* application:
*
* 这里是说这个是引导和加载Spring Applicaiton的Java 主方法,也就是Spring Boot应用的入口.
* 你可以通过默认的类和下面的步骤来引导你的应用。
*
* <ul>
* <li>Create an appropriate {@link ApplicationContext} instance (depending on your
* classpath)</li>
* <li>Register a {@link CommandLinePropertySource} to expose command line arguments as
* Spring properties</li>
* <li>Refresh the application context, loading all singleton beans</li>
* <li>Trigger any {@link CommandLineRunner} beans</li>
* </ul>
*
* In most circumstances the static {@link #run(Class, String[])} method can be called
* directly from your {@literal main} method to bootstrap your application:
*
* <pre class="code">
* @Configuration
* @EnableAutoConfiguration
* public class MyApplication {
*
* // ... Bean definitions
*
* public static void main(String[] args) {
* SpringApplication.run(MyApplication.class, args);
* }
* }
* </pre>
*
* <p>
* For more advanced configuration a {@link SpringApplication} instance can be created and
* customized before being run:
*
* <pre class="code">
* public static void main(String[] args) {
* SpringApplication application = new SpringApplication(MyApplication.class);
* // ... customize application settings here
* application.run(args)
* }
* </pre>
*
* {@link SpringApplication}s can read beans from a variety of different sources. It is
* generally recommended that a single {@code @Configuration} class is used to bootstrap
* your application, however, you may also set {@link #getSources() sources} from:
* <ul>
* <li>The fully qualified class name to be loaded by
* {@link AnnotatedBeanDefinitionReader}</li>
* <li>The location of an XML resource to be loaded by {@link XmlBeanDefinitionReader}, or
* a groovy script to be loaded by {@link GroovyBeanDefinitionReader}</li>
* <li>The name of a package to be scanned by {@link ClassPathBeanDefinitionScanner}</li>
* </ul>
*
* Configuration properties are also bound to the {@link SpringApplication}. This makes it
* possible to set {@link SpringApplication} properties dynamically, like additional
* sources ("spring.main.sources" - a CSV list) the flag to indicate a web environment
* ("spring.main.web-application-type=none") or the flag to switch off the banner
* ("spring.main.banner-mode=off").
*
* @since 1.0.0
* @see #run(Class, String[])
* @see #run(Class[], String[])
* @see #SpringApplication(Class...)
*/
public class SpringApplication {
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
......
首先我们来看它的构造方法
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #SpringApplication(ResourceLoader, Class...)
* @see #setSources(Set)
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@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));
//判断启动容器的类型--查看WebApplicationType类的deduceFromClasspath();一共三种类型
//1.WebApplicationType.NONE; 2.WebApplicationType.SERVLET 3.WebApplicationType.REACTIVE;
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化Bean和监听器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//决定Application的MainClass
this.mainApplicationClass = deduceMainApplicationClass();
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
为什么我们传入了Main Class还需要再判断Main Class呢?——推断引导类
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;
}
加载应用上下文初始器
就是这一段
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.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
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
实现类:SpringFactoriesLoader
配置资源:META-INF/spring.factories (可能在jar包里)
SpringApplication运行阶段
加载:SpringApplication 运行监听器
运行:SpringApplication 运行监听器
监听:SpringBoot事件,Spring 事件
创建:应用上下文,Environment,其他(不重要)
失败:故障报告
回调:CommandLineRunner,ApplicaitonRunner
看一下SpringApplication类的run方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
启动Spring Application,创建并且刷新一个新的Application上下文
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//加载运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}