关于 SpringBoot 你知道多少
1、简介
? ?SpringBoot 从本质上看就是 Spring,它只是帮你做了 那些你 必须去做 又 很繁琐 的 Spring Bean 配置。Spring Boot 本身不提供 Spring 的核心功能,而是作为 Spring 的脚手架框架,使用 “约定优于配置” 的理念,达到 快速构建项目、预置三方配置、开箱即用 的目的。
补充:
? ?SpringBoot 两大策略:
-
开箱即用(Out of box) ? ?指在开发过程中,通过在 Maven 项目的 pom 文件中添加 相关依赖包,然后使用 对应注解 来替代 繁琐的 xml 配置文件 以管理对象的生命周期。 -
约定优于配置(Convention over configuration) ? ?是一种由 SpringBoot 本身来配置 目标结构,由 开发者 在结构中添加信息 的 软件设计范式。
? ?SpringBoot 的优点:
-
可以创建 基于 Maven(或 Gradle)的、独立的 Spring 应用程序,生成 可执行的 Jar 包(或 War包) -
内嵌 Tomcat(或 Jetty)等 Servlet 容器 -
提供 自动配置 的 “starter” 项目对象模型(Project Object Model,简称 POM,定义了 对其它库的 传递依赖)以简化 Maven 配置 -
尽可能自动配置 Spring 容器 -
提供 运行时的应用监控 -
与 云计算 天然集成
2、SpringBoot 的启动流程
? ?SpringBoot 项目创建完成会默认生成一个名为 xxxApplication 的入口类,通过该类的 main 方法可以启动 SpringBoot 项目。
? ?在 main 方法中,通过 SpringApplication 的静态方法(run 方法)进行 SpringApplication 类的实例化操作,再调用 实例化对象 的 run 方法 来完成 整个项目的初始化和启动。
扩展:
? ?SpringApplication 实例化对象的 run() 源代码
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
return context;
}
3、SpringBoot 的自动装配
3.1、SpringBoot 的自动装配:
? ?当我们使用 SpringBoot 时,只需要引入对应的 Starters 依赖,SpringBoot 启动时便会自动加载相关依赖,配置相应的初始化参数,以最快捷、简单的形式对 第三方软件 进行集成。
3.2、SpringBoot 的自动装配流程:
? ?SpringBoot 通过 @EnableAutoConfiguration 开启自动配置,加载 spring.factories 中注册的各种 AutoConfiguration 类,当某个 AutoConfiguration 类 满足器注解 @Conditional 指定的生效条件(Starters 提供的依赖、配置 或 Spring 容器中 是否存在某个 Bean 等)时,实例化该 AutoConfiguration 类 中定义的 Bean(组件等),并注入 Spring 容器,就可以完成 依赖框架的自动配置。
4、SpringBoot 的注解
注解 | 说明 |
---|
@SpringBootApplication | SpringBoot 的启动注解 | @EnableAutoConfiguration | SpringBoot 的自动配置注解 | @Import | SpringBoot 的加载配置类注解 | @Conditional | SpringBoot 的前置条件判断注解 |
补充:@Conditional 的衍生注解
注解 | 说明 |
---|
@ConditionalOnProperty | 在指定的属性有指定值的条件下 | @ConditionalOnBean | 在容器中有指定 Bean 的条件下 | @ConditionalOnMissingBean | 在容器中没有指定 Bean 的条件下 | @ConditionalOnSingleCandidate | 当指定的 Bean 在容器中只有一个(或多个)但指定了primary(首选) Bean 的条件下 | @ConditionalOnClass | 在 classpath(类路径)有指定类的条件下 | @ConditionalOnMissingClass | 在 classpath(类路径)没有指定类的条件下 | @ConditionalOnResource | 在 classpath(类路径)有指定资源的条件下 | @ConditionalOnExpression | 在多个配置(表达式)满足 SpEL 表达式 的条件下 | @ConditionalOnJava | 在运行环境符合指定 Java 版本的条件下 | @ConditionalOnJndi | 在指定资源通过 JNDI 加载的条件下 | @ConditionalOnCloudPlatform | 在运行环境符合指定 云平台 的条件下 | @ConditionalOnWebApplication | 在项目是一个 Web 项目的条件下 | @ConditionalOnNotWebApplication | 在项目不是一个 Web 项目的条件下 |
5、深入分析 SpringBoot 的自动装配
5.1、@SpringBootApplication
? ?SpringBoot 的自动装配机制由 @SpringBootApplication 集中体现,@SpringBootApplication 主要包含了以下的 3 个注解。
- SpringBootConfiguration(本质是 @Configuration 配置类)
- EnableAutoConfiguration(核心注解,负责启动 SpringBoot 的自动装配机制)
- ComponentScan(启动 @Component 组件的扫描,扫描时 过滤掉 指定的类)
扩展:
? @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan
5.2、@SpringBootConfiguration
? ?@SpringBootConfiguration 的作用是:标注这个类是一个 SpringBoot 的配置类,@SpringBootConfiguration 主要包括 @Configuration。
5.3、@EnableAutoConfiguration
? ?@EnableAutoConfiguration 是 SpringBoot 自动装配机制的核心注解,负责启动 SpringBoot 的自动装配机制,@EnableAutoConfiguration 主要包含以下 2 个注解。
- @AutoConfigurationPackage(指定默认的包规则)
- @Import(AutoConfigurationImportSelector.class)(自动配置的后续实现类)
5.3.1、@AutoConfigurationPackage
? ?@AutoConfigurationPackage 的作用是:将 添加该注解的类所在的 package 作为自动配置 package 进行管理,通过 @Import 将 package 注入到 IoC 容器中。
? 补充:
AutoConfigurationPackages.Registrar:
? ?registerBeanDefinitions() 可以将 指定包中的所有组件 注册到容器中。
AutoConfigurationPackages.PackageImports:
? ?ClassUtils.getPackageName() 可以获取 标注 @AutoConfigurationPackage 注解的类的全限定名。
5.3.2、@Import(AutoConfigurationImportSelector.class)
- @Import 加载 AutoConfigurationImportSelector 类
- AutoConfigurationImportSelector 类 实现了 DeferredImportSelector 接口,DeferredImportSelector 接口 继承了 ImportSelector 接口
- selectImports() 会去扫描 META_INF/Spring.factories 配置文件,并将其中的组件完成自动装配
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
5.3.2.1、获取自动装配类的候选类名集合
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
while(urls.hasMoreElements()) 会执行三次循环:
5.3.2.2、触发自动装配的导入事件
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
5.4、@ComponentScan
? ?@ComponentScan 的作用是:扫描 当前类所在的包 及其子包下 包含的注解,将 @Controller / @Service / @Component / @Repository 等注解 加载到 IoC 容器中。
5.5、总结
? ?SpringBoot 自动装配 的本质是:通过 Spring 去读取 META-INF/spring.factories 配置文件 并 根据配置文件中的 k-v 键值对(接口 全限定名 - 实现类 全限定名) 批量加载 bean 的过程。
? 补充:spring.factories
|