自动配置原理
概述
Spring Boot 内部集成了大量第三方服务,提供了默认配置,而默认配置生效的步骤:
- @EnableAutoConfiguration 注解会去所有包下寻找 META-INF/spring.factories 文件,读取其中以 EnableAutoConfiguration为 key 的所有类的名称,这些类就是提前写好的自动配置类
- 这些类都声明了 @Configuration注解,并且通过 @Bean 注解提前配置了所需要的一切实例
- 但是,这些配置不一定生效,因为有 @Conditional注解,满足一定条件才会生效
- 只需要引入了相关依赖(启动器),上面的配置需要的条件成立,自动配置生效
- 如果自己配置了相关Bean,那么会覆盖默认的自动配置的Bean
- 还可以通过配置 application.properties(yml) 文件,来覆盖自动配置中的属性
Spring Boot 能够快速开发的原因就在于内置了很多第三方组件的默认配置,使用的步骤如下:
- 使用启动器
如果不想自己配置,只需要引入启动器依赖即可,而依赖版本也不用操心,因为只要引入了 Spring Boot 提供的 stater(启动器),就会自动管理依赖及版本了。
- 替换默认配置
SpringBoot 的配置,都会有默认属性,而这些属性可以通过修改 application.properties 文件来进行覆盖。
关键注解
@Conditional
@Condition 是在 Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作
@Conditional 标注在配置类上或配置类的方法上,和 @Configuration 配合使用,@Conditional 指定的条件成立,配置类里面的内容才生效
SpringBoot常用条件注解:
- @ConditionalOnBean:容器中存在指定Bean
- @ConditionalOnMissingBean :容器中不存在指定Bean
- @ConditionalOnProperty:系统中指定的属性是否有指定的值
- @ConditionalOnClass :系统中有指定的类
- @ConditionalOnMissingClass: 系统中没有指定的类
- @ConditionalOnExpression :满足SpEL表达式指定
- @ConditionalOnSingleCandidate :容器中只有一个指定的Bean,或者这个Bean是首选Bean
- @ConditionalOnResource :类路径下是否存在指定资源文件
- @ConditionalOnWebApplication :当前是web环境
- @ConditionalOnNotWebApplication :当前不是web环境
- @ConditionalOnJava:系统的java版本是否符合要求
示例:
User类
@Data
public class User {
private String username;
private Integer age;
}
配置类
@Configuration
@ConditionalOnProperty(value = "user.enable") // 配置文件存在该配置项时该配置类才生效
public class UserConfig {
@Bean
public User user(){
User user = new User();
user.setUsername("tom");
user.setAge(18);
return user;
}
}
配置文件中添加如下配置:
user:
enable: true
添加测试用例
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTests {
@Autowired
private User user;
@Test
public void testUser(){
System.out.println(user);
}
}
SpringBoot自动配置流程图
使用的时候只需要引入相应的starter,starter是一个指示信号,引入这个依赖之后,相应的条件满足了,就会注册相应的Bean。
@Import
参考:
@Import 注解:把类导入 Spring IOC容器
有多种方式能让类加 IOC容器管理,如@Bean、@Component等,@Import 是另外一种方式,更加快捷。
支持三种方式:
- 带有 @Configuration 的配置类(4.2版本之前只可以导入配置类,4.2版本之后 也可以导入普通类)
- ImportSelector 的实现
- ImportBeanDefinitionRegistrar 的实现
主要用法:
直接填 class 数组方式
@Configuration @Import({User.class}) // 大括号中可以添加多个类,使用逗号分隔,例如 {User.class,UserInfo.class} public class UserConfig {}
ImportSelector 方式(Spring Boot 底层采用比较得多的方式)
// 自定义ImportSelector public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 返回的是需要加载的类名数组 注意这里需要的是类的全路径 return new String[]{"com.itheima.redis.entity.User"}; } }
@Configuration @Import(MyImportSelector.class) public class UserConfig {}
ImportBeanDefinitionRegistrar 方式
这种方式和 ImportSelector 方式类似,不过这种方式可以自定义Bean在容器中的名称
// 自定义ImportBeanDefinitionRegistrar public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestD.class); //自定义注册bean registry.registerBeanDefinition("testD1111",rootBeanDefinition); } }
@Import({TestImportBeanDefinitionRegistrar.class}) @Configuration public class ImportConfig {}
- 注意:三种用法方式都可在一个@Import中使用,需要注意的是class 数组方式、ImportSelector 方式在IOC容器中bean名称是类的全限定类名,而ImportBeanDefinitionRegistrar 方式是自定义的名称
@SpringBootApplication
流程图
查看源码
重点的注解有3个:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
@SpringBootConfiguration
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
这个注解上面有一个 @Configuration 注解,@Configuration 注解的作用为声明当前类是一个配置类,然后 Spring 会自动扫描到添加了@Configuration的类,并且读取其中的配置信息。
@SpringBootConfiguration 是来声明当前类是 SpringBoot 应用的配置类 。
@EnableAutoConfiguration
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
借助@Import(AutoConfigurationImportSelector.class)
,@EnableAutoConfiguration
可以帮助 Spring Boot 应用将所有符合条件的 @Configuration
配置都加载到当前 Spring Boot 创建并使用的IOC容器。
AutoConfigurationImportSelector
源码,查看selectImports
方法:
- 继续跟进 getAutoConfigurationEntry 方法
- 在
getCandidateConfigurations
方法中有一段提示:
- 跟进
loadFactoryNames
方法:
- 可以看到从这个位置加载配置:
- 找到配置文件
这里有自动配置:
在自动配置包中内置了大量的第三方中间件的配置类:
总结:@EnableAutoConfiguration
从 classpath 中搜寻所有 META-INF/spring.factories
配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的配置项通过反射实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IOC 容器配置类,然后汇总为一个,并加载到IOC容器。
@ComponentScan
开启组件扫描。提供了类似与 <context:component-scan>
标签的作用
通过 basePackageClasses 或者 basePackages 属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包
而@SpringBootApplication注解声明的类就是 main 函数所在的启动类,因此扫描的包是该类所在包及其子包。因此,一般启动类会放在一个比较前的包目录中
SpringBoot 监控
Actuator
概念
SpringBoot 自带监控功能 Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性、日志信息等;同时也可以通过Actuator管理应用程序,例如通过Actuator去做一个shutdown功能(默认是不开启的),另外还可以在运行的过程中对日志进行调整。
Endpoints:端点
可以把 Endpoints 理解为一个功能模块,功能模块可以监控 Spring Boot 应用,甚至可以与 Spring Boot 进行交互(比如读取信息,关闭应用等操作)。Spring Boot内置了很多 Endpoints,最重要的 Endpoints 是 health,即健康检查模块。
Actuator端点说明
端点 | 说明 | 默认开启 | 默认 HTTP | 默认 JMX |
---|---|---|---|---|
auditevents | 公开当前应用程序的审查事件信息 | Y | N | Y |
beans | 显示Spring loC容器关于Bean的信息 | Y | N | Y |
caches | 显示应用中的缓存 | Y | N | Y |
conditions | 显示自动配置类的评估和配置条件,并且显示他们匹配或者不匹配的原因 | Y | N | Y |
configprops | 显示当前项目的属性配置信息(通过@ConfigurationProperties配置) | Y | N | Y |
env | 显示当前Spring应用环境配置属性(ConfigurableEnvironment) | Y | N | Y |
flyway | 显示已经应用于flyway数据库迁移的信息 | Y | N | Y |
health | 显示当前应用健康状态 | Y | Y | Y |
httptrace | 显示最新追踪信息(默认为最新100次 HTTP请求) | Y | N | Y |
info | 显示当前应用信息 | Y | Y | Y |
loggers | 显示并更新应用程序中记录器的配置 | Y | N | Y |
liquibase | 显示已经应用于liquibase数据库迁移的信息 | Y | N | Y |
metrics | 显示当前配置的各项“度量”指标 | Y | N | Y |
mappings | 显示由@RequestMapping (@GetMapping和@PostMapping 等)配置的映射路径信息 | Y | N | Y |
scheduledtasks | 显示当前应用的调度任务计划 | Y | N | Y |
sessions | 允许从Spring会话支持的会话存储库检索和删除用户会话,只是Spring 会话对响应式Web 应用还暂时不能支持 | Y | N | Y |
shutdown | 允许当前应用被优雅地进行关闭(在默认的情况下不启用这个端点) | N | N | Y |
threaddump | 显示线程泵 | Y | N | Y |
heapdump | 返回 Heap Dump 文件,格式为 HPROF | Y | N | N/A |
prometheus | 返回可供 Prometheus 抓取的信息 | Y | N | N/A |
注意:
- shutdown 端点默认不启用
- 只有health和 info 默认是可以通过 http 进行访问的
快速入门
依赖坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
如果想看更多的信息,在配置文件中配置:
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: "*"
Actuator监控接口:
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/env
http://localhost:8080/actuator/mappings
具体详细的解释:
路径 | 描述 |
---|---|
/beans | 描述应用程序上下文里全部的Bean,以及它们的关系 |
/env | 获取全部环境属性 |
/env/{name} | 根据名称获取特定的环境属性值 |
/health | 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 |
/info | 获取应用程序的定制信息,这些信息由info打头的属性提供 |
/mappings | 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 |
/metrics | 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 |
/metrics/{name} | 报告指定名称的应用程序度量值 |
/trace | 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等) |
Actuator 的详细配置
management:
server:
# Actuator的管理端口号,默认跟服务端口号(server.port)一致
port: 8085
endpoint:
health:
# 是否开启health端点,默认为true
enabled: true
# 展示健康检查中所有指标的详细信息
show-details: always
endpoints:
# web:http方式
web:
exposure:
# 暴露可以通过web访问的端点。默认"health,info",用 * 可以包含全部端点
include: "*"
# 不暴露的端点
exclude:
# 自定义监控路径前缀。默认 /actuator
base-path: /actuator
# 修改端点的访问路径(映射),例如将 /beans 更改为 /request_beans
path-mapping:
beans: request_beans
# 所有端点是否默认启动,默认true。若设置为false,则默认情况下所有端点都不启用,此时需要按需启用端点
enabled-by-default: true
Spring Boot Admin
概述及快速入门
Actuator 的监控内容够详细,但是阅读性比较差,所以可以使用 Spring Boot Admin 提供一个可视化的界面查阅信息,Spring Boot Admin 是一个第三方提供的开源社区项目,用于管理和监控 SpringBoot 应用程序。
源码库:https://github.com/codecentric/spring-boot-admin
Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server):
- 应用程序作为 Spring Boot Admin Client 向为 Spring Boot Admin Server 注册
- Spring Boot Admin Server 的界面将展示 Spring Boot Admin Client 的监控信息
开发步骤如下:
admin-server:
- 创建 admin-server 模块
导入依赖坐标 admin-starter-server
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <version>2.3.0</version> </dependency>
- 在启动类上标注 @EnableAdminServer 注解,启用 Admin 监控功能
配置相关信息
server: port: 8888
- 启动 server 服务,访问:http://localhost:8888/#/applications
admin-client: 自己创建的项目就是所谓的client端
- 创建 admin-client 模块
导入依赖坐标 admin-starter-client
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.3.0</version> </dependency>
配置相关信息:server地址等
spring: boot: admin: client: url: http://localhost:8888 management: endpoint: health: show-details: always endpoints: web: exposure: include: "*"
- 启动 client 服务
Admin Server 的详细配置
参考:https://www.jianshu.com/p/b0528b52772c
spring:
boot:
admin:
# server端的访问路径。默认为 /
context-path: /
monitor:
# 更新client端状态的时间间隔,默认为10000,单位是毫秒
period: 10000
# client端状态的生命周期,该生命周期内不会更新client状态,默认为100000,单位是毫秒
status-lifetime: 100000
# 查询client状态信息时的连接超时时间,默认为2000,单位是毫秒
# 如果2秒内没有获取到client的状态信息,则认为连接已经断开
connect-timeout: 2000
# 查询client状态信息时的读取超时时间,默认为2000,单位是毫秒(如果2秒内没有获取到client的状态信息,则认为读取失败)
read-timeout: 2000
# 要被过滤掉的元数据(当与正则表达式相匹配时,这些数据会在输出的json数据中过滤掉)
# 默认值是".password", ".secret",".∗secret", ".key", ".",".token", ".credentials.", ".*vcap_services", ".credentials.", ".∗vcap services"
metadata-keys-to-sanitize:
# 要获取的client的端点信息
# 默认是 "health", "env", "metrics", "httptrace:trace", "threaddump:dump", "jolokia", "info", "logfile", "refresh", "flyway", "liquibase", "heapdump", "loggers", "auditevents"
probed-endpoints:
instance-proxy:
# 向client发起请求时不会被转发的headers信息。默认值是"Cookie", "Set-Cookie", "Authorization"
ignored-headers:
ui:
# 在导航栏中显示的brand值
brand:
# 显示的页面标题。默认是"Spring Boot Admin"
title:
Admin Client 的详细配置
参考:https://www.jianshu.com/p/b0528b52772c
spring:
boot:
admin:
client:
# 是否启用spring boot Admin客户端,默认为true
enabled: true
# 要注册的Admin Server端的url地址。如果要同时在多个server端口注册,则用逗号分隔各个server端的url地址
url: http://localhost:8888
# server端获取client信息的路径,默认情况下server通过访问/instances请求来获取到client端的信息。(client端向server端注册,注册成功后server端会给该client创建一个唯一的clientID值。当server端需要获取client的信息,比如health信息时,server端会发送http://IP:PORT/instances/clientID/actuator/health即可,这里的http://IP:PORT是client所在服务器的IP地址,instances就是该属性的值)
api-path: instances
# 如果server端需要进行认证时,该属性用于配置用户名
username: user
# 如果server端需要进行认证时,该属性用于配置密码
password: 123456
# 注册时间间隔,默认10000,单位是毫秒(client通过持续不断地向server端进行注册来保持client端与server端的连接)
period: 10000
# 注册连接超时时间,默认5000,单位是毫秒。当client向server进行注册时,如果5秒钟没有注册完成则认为本次注册失败
connect-timeout: 5000
# 注册读取超时,默认5000,单位是毫秒
read-timeout: 5000
# 是否开启自动注册,默认为true
auto-registration: true
# 是否开启自动注销,如果服务端运行在云平台,默认值是true
auto-deregistration: null
# 默认为true,client只会在一个server端进行注册(按照spring.boot.admin.client.url中设置的server的顺序),如果该server端宕机,会自动在下一个server端进行注册。如果该属性值为false,则会在所有的server端进行注册
egister-once: true
instance:
# 注册的management-url,如果可用的url不同的话,可以重写该值。
# 默认该属性值与management-base-url 和 management.context-path两个属性值有关
management-url:
# 用于计算management-url的基本URL。该路径值在运行时进行获取并赋值给 base url
# 默认该属性值与management.port, service-url 以及server.servlet-path有关
management-base-url:
# 用于计算service-url的基本URL。该路径值在运行时进行获取并赋值给 base url
service-base-url:
# 注册的health-url地址,如果可用的url不同可以重写该值
health-url:
# 注册的service-url值
service-url: http://192.168.0.66:22586
# 客户端工程的名字。默认值是配置的spring.application.name的值
name:
# 是否使用注册的ip地址来取代上述各个url中hostname的值,默认为false
prefer-ip: true
自定义封装 Starter 启动器
启动器一般都是以spring-boot-starter-
开头,这种方式一般都是 Spring 官方的命名方式。
其他企业开发的启动器命名方式类似于:XXX-boot-starter
启动器一般都是一个空壳子,里面没有代码,主要用于管理依赖
开发步骤:
- 创建 starter 项目,示例:test-hello-spring-boot-starter
创建自动配置项目,示例:test-spring-boot-autoconfigure
(1)添加基本的springboot依赖(因为要用@Bean等注解)
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies>
(2)定义自动配置类,示例:在配置类中将HelloTemplate定义为一个Bean
@Configuration public class HelloAutoConfiguration { @Bean //实例化HelloTemplate,交给Spring IOC 容器管理 public HelloTemplate helloTemplate(){ return new HelloTemplate(); } }
(3)在resources下创建META-INF目录,在META-INF目录下创建 spring.factories 文件,在 spring.factories 中编辑要扫描的配置类(HelloAutoConfiguration),这样spring就会扫描到 HelloAutoConfiguration 了
# 第一行固定写法,第二行这个是我们写的配置类的类名全路径 这样spring就能扫描到了 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.itheima.hellospringbootdemo.util.HelloAutoConfiguration
在 starter 项目的 pom 文件中添加自动配置项目的依赖
<dependencies> <dependency> <groupId>com.test</groupId> <artifactId>test-spring-boot-autoconfigure</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
- 在其它工程中添加 stater 项目的依赖,即可通过 @Autowired 使用自动配置项目中注入到Spring容器的 Bean
SpringBoot 项目的部署
项目打包
在SpringBoot项目中,都是通过将项目打成 jar 包,然后直接运行(因为内置了tomcat)
- 准备一个开发完的 SpringBoot 项目,因为是 SpringBoot 项目,所以一定要有启动类
在准备打包的项目中的 pom 文件中,添加如下内容
<build> <!--finalName 可以不写,写了就是要打包的项目的名称 --> <finalName>demo</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
- 找到 idea 右侧的 maven 区域,找到想打包的项目,点击clean,清除缓存,如图
如果点击test,会将在 test/java 下所有的测试用例都跑一边,所有测试用例都通过后才会执行后面的流程,这里的流程都是下一步会包含上一步。
如果在打包时不想运行测试用例,有两种方式跳过:
方式1:可以在 pom 中添加如下设置:
<properties> <!--打包时跳过测试用例--> <skipTests>true</skipTests> </properties>
- 方式2:在IDEA中配置如下
- 点击 package 进行打包操作,最后显示
BUILD SUCCESS
即为打包成功注意:如果项目有自定义的依赖,则需要先点击 install,将依赖包安装到本地仓库;否则若直接点击package,会报找不到类等错误
- 在 idea 左侧项目的 target 目录下,可以找到刚打好的 jar 包
- 在 jar 包文件夹地址栏上输入cmd,打开命令窗口,在命令行中输入
java -jar 项目名.jar
,回车即可启动应用
Spring Boot 多环境配置切换
一般在一个项目中,总是会有好多个环境。比如:开发环境 -> 测试环境 -> 预发布环境 -> 生产环境。
每个环境上的配置文件总是不一样的,甚至开发环境中每个开发者的环境可能也会有一点不同。
在 Spring Boot中,多环境配置的文件名需要满足 application-{profile}.yml 或者 application-{profile}.properties的格式,其中{profile} 对应环境,比如:
- application-dev.yml:开发环境
- application-test.yml:测试环境
- application-prod.yml:生产环境
配置环境的常用方式:
方式1:直接在
application.yml
中配置:spring: profiles: active: dev
- 方式2:在 IDEA 中指定配置生效:
方式3:在启动时添加参数来指定使用哪个配置
# 使用测试环境配置文件 java -jar xxx.jar --spring.profiles.active=test
Spring Boot 配置加载顺序
Spring Boot 若在优先级更高的位置找到了相同的配置,那么它就会无视低级的配置。如果不同的配置是可以同时生效的。
根据 Spring Boot 的文档,配置使用的优先级从高到低的顺序,具体如下所示:
- 命令行参数
- 通过 System.getProperties() 获取的 Java 系统参数
- 操作系统环境变量
- 从 java:comp/env 得到的 JNDI 属性
- 通过 RandomValuePropertySource 生成的 "random.*" 属性
- 应用 Jar 文件之外的属性文件(application-{profile}.properties/yml)
- 应用 Jar 文件内部的属性文件(application-{profile}.properties/yml)
- 应用 Jar 文件之外的属性文件(application.properties/yml)
- 应用 Jar 文件内部的属性文件(application.properties/yml)
- 在应用配置 Java 类(包含“@Configuration”注解的 Java 类)中通过 "@PropertySource" 注解声明的属性文件
- 通过 "SpringApplication.setDefaultProperties" 声明的默认属性
Spring Boot 加载外部的配置文件
方式1:通过环境变量 spring.config.location 指定
注意:使用 location 参数指定配置文件后,会使项目默认配置文件(application.properties 或 application.yml )失效,Spring Boot 将只加载指定的外部配置文件
java -jar springbootdemo-0.0.1-SNAPSHOT.jar --spring.config.location=./my-application.yml
方式2:通过环境变量 spring.config.additional-location 指定
注意:使用 additional-location 参数不会使项目默认的配置文件失效,外部配置文件会与项目默认的配置文件共同生效,形成互补配置,且其优先级比所有默认配置文件的优先级都高
java -jar springbootdemo-0.0.1-SNAPSHOT.jar --spring.config.additional-location=./my-application.yml