🦉 SpringBoot
2022年10月10日
- frame
🦉 SpringBoot
1. 概述
- SpringBoot 基于 Spring 开发,SpringBoot 本身并不提供 Spring 框架的核心特性 & 扩展功能
- SpringBoot 只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序
- SpringBoot 并不是用于替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具
优点
- SpringBoot 遵循 约定大于配置 的核心思想,与 Spring 相比,它有以下优势:
- 起步依赖
- 自动装配
- 将原有的 xml 配置改为 Java 配置,将 bean 注入改为 注解注入,并将多个 xml、properties 配置集成在一个
application.yml
中
- 将原有的 xml 配置改为 Java 配置,将 bean 注入改为 注解注入,并将多个 xml、properties 配置集成在一个
- 端点监控
- 基于 actuator 实现
- SpringBoot 可 快速创建 独立的 Spring 应用程序
- SpringBoot 内嵌
Tomcat
等容器,因此可以 直接启动,简化部署 - SpringBoot 提供了一些现有的功能,例如表单数据验证等第三方功能
- SpringBoot 可快速整合常用依赖,提供
pom
简化了 maven 的配置
2. 自动装配
1) 原理
- 启动类
- 依赖于
@SpringBootApplication
注解
- 依赖于
@SpringBootApplication
@SpringBootApplication
是个 复合注解- 实现 自动装配 重点依赖于其中一个注解
@EnableAutoConfiguration
@EnableAutoConfiguration
- 核心是
@Import({AutoConfigurationImportSelector.class})
注解 - 通过导入
AutoConfigurationImportSelector
类实现 自动装配 逻辑
- 核心是
AutoConfigurationImportSelector
类- 从
META-INF/spring.factories
中获取 所有满足条件的自动装配类 的路径
- 从
2) 自定义 SpringBoot Starter
- 创建一个项目,作为自定义的 SpringBoot Starter 类
- 引入 SpringBoot 相关依赖
- 编写配置文件类
- 创建自动配置类
- 配置自动类
- 在
/resources/META-INF/spring.factories
文件中添加 自动配置类 路径
- 在
- 测试
- 创建一个新项目,导入 自定义 SpringBoot Starter 依赖
- 在配置文件中添加配置
- 编写测试类进行测试
3. 启动原理
基于 SpringBoot 2.x
1) ManageApplication 启动类
package com.manage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) {
SpringApplication.run(ManageApplication.class, args);
}
}
2) SpringApplication.run(args)
public class SpringApplication {
// ...
// 属性
private Set<Class<?>> primarySources;
private Set<String> sources;
private Class<?> mainApplicationClass;
private Mode bannerMode;
private boolean logStartupInfo;
private boolean addCommandLineProperties;
private boolean addConversionService;
private Banner banner;
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
private WebApplicationType webApplicationType;
private boolean headless;
private boolean registerShutdownHook;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Map<String, Object> defaultProperties;
private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
private Set<String> additionalProfiles;
private boolean allowBeanDefinitionOverriding;
private boolean isCustomEnvironment;
private boolean lazyInitialization;
private String environmentPrefix;
private ApplicationContextFactory applicationContextFactory;
private ApplicationStartup applicationStartup;
// 构造函数
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 属性赋初值
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 判断应用程序类型,分为 Servlet, Reactive, None 三种枚举类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从 SpringFactories 加载 初始化器
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
// 设置 应用上下文初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置 应用上下文监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 找到 main 方法,设置为 运行的主类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
// 从 SpringFactories 加载 初始化器
private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList();
this.getSpringFactoriesInstances(Bootstrapper.class).stream().map((bootstrapper) -> {
return bootstrapper::initialize;
}).forEach(initializers::add);
initializers.addAll(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
return initializers;
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 创建 SpringFactory 集合实例
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes,
ClassLoader classLoader,
Object[] args,
Set<String> names) {
List<T> instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
while(var7.hasNext()) {
String name = (String)var7.next();
try {
// 通过 类名 & 类加载器 获取 类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 获取 类构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 内部调用 反射 生成该 SpringFactory
T instance = BeanUtils.instantiateClass(constructor, args);
// 添加到结果集中
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
// 找到 main 方法,设置为 运行的主类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}
public ConfigurableApplicationContext run(String... args) {
// 1. 启动计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 系统属性设置
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
// 3. 初始化监视器并启动,发布 ApplicationStartedEvent 事件
// 1) 获取所有 监视器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 2) starting 函数 - 执行 multicastEvent 方法,唤醒所有的 事件监听器
// multicastEvent 方法 会执行 invokeListener 方法
// 会执行 invokeListener 方法 会 调用 listener.onApplicationEvent(event),只要事件监听器重写了该方法,那么就会执行 具体事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4. 环境配置,发布 ApplicationEnvironmentPreparedEvent 事件
// 在这一步中,会触发相关监视器 ConfigFileApplicationListener,加载 properties & yml 文件
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 5. 打印 banner
Banner printedBanner = this.printBanner(environment);
// 6. 创建 ApplicationContext
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 7. 上下文 前置处理,装配 Context 的环境变量等
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 8. 刷新 上下文 - 继续进入
this.refreshContext(context);
// 9. 上下文刷新的后置处理
this.afterRefresh(context, applicationArguments);
// 10. 计时器 结束
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 11. 监视器启动
listeners.started(context);
// 12. 执行 runner 运行器运行
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
// 13. 监视器运行
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
}
4. .properties VS yml
1) 加载时间
- 在
run()
中的ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
方法会进行加载
2) .properties VS yml
.properties | yml | |
---|---|---|
语法格式 | spring.datasource.username=root | spring: datasource: username: root |
数据类型 | 基础数据类型 | 对象 、数组 均可 |
语言 | SpringBoot | Java PHP Python JavaScript Golang |
- yml 文件复杂数据结构的表示
# 数组 & 对象 应用举例
pili:
figures:
- liuxianzhishou
- tunfo
- xiao
- fojian
favoritefigure:
name: xiao
location: AoFeng