Spring Boot特性与模块
特性
Spring Boot 是构建基于Spring的应用程序的起点。Spring Boot 旨在让你尽可能快地启动和运行,并以最小的预先配置的Spring配置。
Spring Boot的核心特性如下:
- 创建一键运行的Spring应用。
- 能够使用内嵌的Tomcat、Jetty或Undertow,不需要部署war。
- 提供定制化的启动器starters简化第三方依赖配置。
- 追求极致的自动配置Spring。
- 提供一些生产环境的特性,比如特征指标、健康检查和外部配置。
- 零代码生成和零XML配置
Spring Boot框架中还有两个非常重要的策略:开箱即用和约定优于配置。
- 开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。
- 约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。
核心模块
Spring Boot是基于Spring Framework体系来构建的,它的主要核心:
- Starter组件,提供开箱即用的组件。
- 自动装配,自动根据上下文完成Bean的装配。
- Actuator,Spring Boot应用的监控。
- Spring Boot CLI,基于命令行工具快速构建Spring Boot应用。
- spring-boot:Spring Boot核心工程。
- starters:Spring Boot的启动服务工程。spring-boot中内置提供的starter列表可以在SpringBoot 项目源代码工程spring-boot/spring-boot-starters中看到。
- autoconfigure:Spring Boot实现自动配置的核心工程。
- actuator:提供Spring Boot应用的外围支撑性功能。比如:应用状态监控管理、应用健康指示表、远程shell支持。
- tools:提供了Spring Boot开发者的常用工具集。
- cli:Spring Boot命令行交互工具,可用于使用Spring进行快速原型搭建。可以用它直接运行Groovy脚本。如果你不喜欢Maven或Gradle,可用CLI(Command Line Interface)来开发运行Spring应用程序。可以使用它来运行Groovy脚本,甚至编写自定义命令。
Spring Boot自动装配的原理
自动装配的实现
自动装配在Spring Boot中是通过@EnableAutoConfiguration注解来开启的,这个注解的声明在启动类注解@SpringBootApplication内。
@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}
)}
)
public @interface SpringBootApplication {}
进入@EnableAutoConfiguration注解里,可以看到除@Import注解之外,还多了一个@AutoConfigurationPackage注解(它的作用是把使用了该注解的类所在的包及子包下所有组件扫描到SpringIoC容器中)。并且,@Import注解中导入的并不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
AutoConfigurationImportSelector实现了ImportSelector,它只有一个selectImports抽象方法,并且返回一个String数组,在这个数组中可以指定需要装配到IoC容器的类,当在@Import中导入一个ImportSelector的实现类之后,会把该实现类中返回的Class名称都装载到IoC容器中。和@Configuration不同的是,ImportSelector可以实现批量装配,并且还可以通过逻辑处理来实现Bean的选择性装配,也就是可以根据上下文来决定哪些类能够被IoC容器初始化。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
例子
//首先创建两个类,我们需要把这两个类装配到IoC容器中。
public class FirstClass {
}
public class SecondClass {
}
//创建一个ImportSelector的实现类,在实现类中把定义的两个Bean加入String数组,
//这意味着这两个Bean会装配到IoC容器中。
public class GpImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{FirstClass.class.getName(),SecondClass.class.getName()};
}
}
//创建一个启动类,在启动类上使用@Import与@AutoConfigurationPackage注解后,
//即可通过ca.getBean从IoC容器中得到FirstClass对象实例。
@SpringBootApplication
@AutoConfigurationPackage
@Import({GpImportSelector.class})
public class DemoApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ca =SpringApplication.run(DemoApplication.class, args);
FirstClass bean = ca.getBean(FirstClass.class);
}
这种实现方式相比@Import(*Configuration.class)的好处在于装配的灵活性,还可以实现批量。
自动装配原理分析
定位到AutoConfigurationImportSelector中的selectlmports方法,它是ImportSelector接口的实现,这个方法中主要有两个功能:
- AutoConfigurationMetadata.loadMetadata方法从META-INF/spring-autoconfigure-metadata.properties中加载自动装配的条件元数据,简单来说就是只有满足条件的Bean才能够进行装配。
- 收集所有符合条件的配置类autoConfigurationEntry.getConfigurations(),完成自动装配。
selectlmports()方法与loadMetadata()方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
//META-INF/spring-autoconfigure-metadata.properties中加载
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
收集所有符合条件的配置类autoConfigurationEntry.getConfigurations()方法
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//获得@EnableAutoConfiguration注解中的属性exclude、excludeName等。
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获得所有自动装配的配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//去除重复的配置项
configurations = this.removeDuplicates(configurations);
//根据@EnableAutoConfiguration注解中配置的exclude等属性,把不需要自动装配的配置类移除
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
//广播事件
this.fireAutoConfigurationImportEvents(configurations, exclusions);
//返回经过多层判断和过滤之后的配置类集合
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
总的来说,它先获得所有的配置类,通过去重、exclude排除等操作,得到最终需要实现自动装配的配置类。 其中getCandidateConfigurations,它是获得配置类最核心的方法。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
这里用到的SpringFactoriesLoader,它是Spring内部提供的一种约定俗成的加载方式,类似于Java中的SPI。它会扫描classpath下的META-INF/spring.factories文件,spring.factories文件中的数据以Key=Value形式存储, 而SpringFactoriesLoader loadFactoryNames会根据Key得到对应的value值。 因此,这个场景中,Key对应为EnableAutoConfiguration,Value是多个配置类,也就是getCandidateConfigurations方法所返回的值。
# AutoConfigureCache auto-configuration imports
org.springframework.boot.test.autoconfigure.core.AutoConfigureCache=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
# AutoConfigureDataJpa auto-configuration imports
org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa=\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
.......
每个XXXAutoConfiguration除了基本的@Configuration注解,还有一个@ConditionalOnClass注解,这个条件控制机制在这里的用途是,判断classpath下是否存在RabbitTemplate和Channel这两个类,如果是,则把当前配置类注册到IoC容器。另外,@EnableConfigurationProperties是属性配置,也就是说我们可以按照约定在applicationproperties中配置RabbitMQ的参数,而这些配置会加载到RabbitProperties中。实际上,这些东西都是Spring本身就有的功能。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
@EnableConfigurationProperties({RabbitProperties.class})
@Import({RabbitAnnotationDrivenConfiguration.class})
public class RabbitAutoConfiguration {
小结
至此,自动装配的原理基本上就分析完了,简单来总结一下核心过程:
- 通过@Import(AutoConfigurationImportSelector)实现配置类的导入,但是这里并不是传统意义上的单个配置类装配。
- AutoConfigurationImportSelector类实现了ImportSelector接口,重写了方法selectImports,它用于实现选择性批量配置类的装配。
- 通过Spring提供的SpringFactoriesLoader机制,扫描classpath路径下的META-INF/springfactories,读取需要实现自动装配的配置类。
- 通过条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。
自动配置
Spring Boot中还提供了spring-autoconfigure-metadata.properties文件来实现批量自动装配条件配置。 它的作用和@Conditional是一样的,只是将这些条件配置放在了配置文件中。下面这段配置来自spring -boot-autoconfigure jar包中的/META-INF/spring-autoconfigure-metadata.properties文件。
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
这种形式也是“约定优于配置”的体现,通过这种配置化的方式来实现条件过滤必须要遵循两个条件:
- 配置文件的路径和名称必须是/META-INF/spring-autoconfigure-metadata.properties。
- 配置文件中key的配置格式:自动配置类的类全路径名条件=值。
这种配置方法的好处在于,它可以有效地降低Spring Boot的启动时间,通过这种过滤方式可以减少配置类的加载数量,因为这个过滤发生在配置类的装载之前,所以它可以降低Spring Boot启动时装载Bean的耗时。
深入Actuator
Spring Boot的Actuator。它提供了很多生产级的特性,比如监控和度量Spring Boot应用程序。Actuator的这些特性可以通过众多REST端点、远程shell和JMX获得。
Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序 运行时的内部状况。有了Actuator,你可以知道Bean在Spring应用程序上下文里是如何组装在一 起的,掌握应用程序可以获取的环境属性信息,获取运行时度量信息的快照……
Actuator端点
要启用Actuator的端点,只需在项目中引入Actuator的起步依赖即可。Actuator提供了13个端点
- GET /autoconfig 提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过
- GET /configprops 描述配置属性(包含默认值)如何注入Bean
- GET /beans 描述应用程序上下文里全部的Bean,以及它们的关系
- GET /dump 获取线程活动的快照
- GET /env 获取全部环境属性
- GET /env/{name} 根据名称获取特定的环境属性值
- GET /health 报告应用程序的健康指标,这些值由 HealthIndicator 的实现类提供
- GET /info 获取应用程序的定制信息,这些信息由 info 打头的属性提供
- GET /mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系
- GET /metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 GET /metrics/{name} 报告指定名称的应用程序度量值
- POST /shutdown 关闭应用程序,要求 endpoints.shutdown.enabled 设置为 true
- GET /trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)
保护 Actuator 端点
很多Actuator端点发布的信息都可能涉及敏感数据,还有一些端点,(比如/shutdown)非常危险,可以用来关闭应用程序。因此,保护这些端点尤为重要,能访问它们的只能是那些经过授权 的客户端。 实际上,Actuator的端点保护可以用和其他URL路径一样的方式——使用Spring Security。在 Spring Boot应用程序中,这意味着将Security起步依赖作为构建依赖加入,然后让安全相关的自动配置来保护应用程序,其中当然也包括了Actuator端点。
Spring Boot开发者工具
Spring Boot引入了一组新的开发者工具,可以让你在开发时更方便地使用Spring Boot, 包括如下功能。
自动重启:当Classpath里的文件发生变化时,自动重启运行中的应用程序。
LiveReload支持:对资源的修改自动触发浏览器刷新。
远程开发:远程部署时支持自动重启和LiveReload。
默认的开发时属性值:为一些属性提供有意义的默认开发时属性值。
Spring Boot的开发者工具采取了库的形式,可以作为依赖加入项目。在Maven POM里添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
当应用程序以完整打包好的JAR或WAR文件形式运行时,开发者工具会被禁用,所以没有必要在构建生产部署包前移除这个依赖。
自动重启
Classpath里对文件做任何修改都会触发应用程序重启。为了让重启速度够快,不会修改的类(比如第三方JAR文件里的类)都加载到了基础类加载器里,而应用程序的代码则会加载到一个单独的重启类加载器里。检测到变更时,只有重启类加载器重启。 有些Classpath里的资源变更后不需要重启应用程序。像Thymeleaf这样的视图模板可以直接编辑,不用重启应用程序。在/static或/public里的静态资源也不用重启应用程序,所以Spring Boot 开发者工具会在重启时排除掉如下目录:/META-INF/resources、/resources、/static、/public和 /templates。 可以设置 spring.devtools.restart.exclude 属性来覆盖默认的重启排除目录。
彻底关闭自动重启,可以将 spring.devtools.restart.enabled设置为false
LiveReload
Spring Boot的开发者工具集成了LiveReload(livereload.com),可以消除刷新的步骤。 Spring Boot会启动一个内嵌的LiveReload服务器,在资源文件变化时会触发浏览器刷新。你要做的就是在浏览器里安装LiveReload插件。 禁用内嵌的LiveReload服务器 , 可以将 spring.devtools.livereload.enabled设置为false 。
远程开发
设置一个远程安全码来开启远程开发功能:
spring:devtools:remote:secret:myappsecret
有了这个属性后,运行中的应用程序就会启动一个服务器组件以支持远程开发。它会监听接受变更的请求,可以重启应用程序或者触发浏览器刷新。 为了使用这个远程服务器,你需要在本地运行远程开发工具的客户端。这个远程客户端是一个类,全限定类名是 org.springframework.boot.devtools.RemoteSpringApplication 。 它会运行在IDE里,要求提供一个参数,告知远程应用程序部署在哪里。
参考
SpringBoot实战(第4版)
Spring Cloud Alibaba 微服务原理与实战