本周(8.21-8.27)将学习芋道 Spring Boot的以下文章:
8.21: 快速入门
8.22:Spring Boot 自动配置原理 、Jar 启动原理
8.23:调试环境、 热部署入门、消除冗余代码 Lombok 入门
8.24:对象转换 MapStruct 入门、SpringMVC 入门
8.25: WebFlux 入门、 分布式 Session 入门
8.26:API 接口文档 Swagger 入门、API 接口文档 Swagger Starter 入门
8.27:参数校验 Validation 入门、WebSocket 入门
芋道 Spring Boot 自动配置原理
-
在 Spring3.0 开始,Spring 提供了 JavaConfig 的方式,允许我们使用 Java 代码的方式,进行 Spring Bean 的创建。
-
通过在类上添加
@Configuration
注解,声明这是一个 Spring 配置类。 -
通过在方法上添加
@Bean
注解,声明该方法创建一个 Spring Bean。 -
通过
@Configuration
注解的配置类,可以解决“创建哪些 Bean”的问题 -
@ConditionalOnWebApplication
条件注解,表示当前配置类需要在当前项目是 Web 项目的条件下,才能生效。 -
@ConditionalOnClass
条件注解,表示当前配置类需要在当前项目有指定类的条件下,才能生效。例如:@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
-
在 Spring Boot 定义了
@ConfigurationProperties
注解,用于声明配置属性类,将指定前缀的配置项批量注入到该类中。例如 :@EnableConfigurationProperties(ServerProperties.class)
-
通过
SpringApplication#run(Class primarySource, String... args)
方法,启动 Spring Boot 应用的时候,有个非常重要的组件 SpringFactoriesLoader 类,会读取META-INF
目录下的spring.factories
文件,获得每个框架定义的需要自动配置的配置类。 -
如此,原先
@Configuration
注解的配置类,就升级成类自动配置类。这样,Spring Boot 在获取到需要自动配置的配置类后,就可以自动创建相应的 Bean,完成自动配置的功能。 -
实际上,自动配置只是 Spring Boot 基于
spring.factories
的一个拓展点 EnableAutoConfiguration。我们从上图中,还可以看到如下的拓展点:- ApplicationContextInitializer
- ApplicationListener
- AutoConfigurationImportListener
- AutoConfigurationImportFilter
- FailureAnalyzer
- TemplateAvailabilityProvider
-
条件注解并不是 Spring Boot 所独有,而是在 Spring3.1 版本时,为了满足不同环境注册不同的 Bean ,引入了
@Profile
注解。@Configuration public class DataSourceConfiguration { @Bean @Profile("DEV") public DataSource devDataSource() { // ... 单机 MySQL } @Bean @Profile("PROD") public DataSource prodDataSource() { // ... 集群 MySQL } }
-
在 Spring4 版本时,提供了
@Conditional
注解,用于声明在配置类或者创建 Bean 的方法上,表示需要满足指定条件才能生效。示例代码如下:@Configuration public class TestConfiguration { @Bean @Conditional(XXXCondition.class) public Object xxxObject() { return new Object(); } }
- 其中,XXXCondition 需要我们自己实现 Condition 接口,提供具体的条件实现。
-
显然,Spring4 提交的
@Conditional
注解非常不方便,需要开发人员自己去拓展。因此,Spring Boot 进一步增强,提供了常用的条件注解:@ConditionalOnBean
:当容器里有指定 Bean 的条件下@ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下@ConditionalOnSingleCandidate
:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean@ConditionalOnClass
:当类路径下有指定类的条件下@ConditionalOnMissingClass
:当类路径下没有指定类的条件下@ConditionalOnProperty
:指定的属性是否有指定的值@ConditionalOnResource
:类路径是否有指定的值@ConditionalOnExpression
:基于 SpEL 表达式作为判断条件@ConditionalOnJava
:基于 Java 版本作为判断条件@ConditionalOnJndi
:在 JNDI 存在的条件下差在指定的位置@ConditionalOnNotWebApplication
:当前项目不是 Web 项目的条件下@ConditionalOnWebApplication
:当前项目是 Web项 目的条件下
-
Spring Boot 约定读取
application.yaml
、application.properties
等配置文件,从而实现创建 Bean 的自定义属性配置,甚至可以搭配@ConditionalOnProperty
注解来取消 Bean 的创建。 -
在使用 Spring Boot 时,并不会直接引入
spring-boot-autoconfigure
依赖,而是使用 Spring Boot 内置提供的 Starter 依赖。因为 Spring Boot 提供的自动配置类,基本都有@ConditionalOnClass
条件注解,判断我们项目中存在指定的类,才会创建对应的 Bean。而拥有指定类的前提,一般是需要我们引入对应框架的依赖。 -
Spring Boot 内置了非常多的 Starter,方便我们引入不同框架,并实现自动配置。所以在引入一个starter后,可能会自动配置上其他的stater
-
在一些场景下,我们需要自己实现自定义 Starter 来达到自动配置的目的。例如说:
- 三方框架并没有提供 Starter,比如说 Swagger、XXL-JOB 等。
- Spring Boot 内置的 Starter 无法满足自己的需求,比如说
spring-boot-starter-jdbc
不提供多数据源的配置。 - 随着项目越来越大,想要提供适合自己团队的 Starter 来方便配置项目,比如说永辉彩食鲜 csx-bsf-all 项目。
-
Spring Boot Starter 的命名规则
场景 命名规则 示例 Spring Boot 内置 Starter spring-boot-starter-{框架}
spring-boot-starter-web
框架 自定义 Starter {框架}-spring-boot-starter
mybatis-spring-boot-starter
公司 自定义 Starter {公司}-spring-boot-starter-{框架}
zt-spring-boot-starter-test -
Spring 的核心之一是 IOC,负责管理 Bean 的生命周期。而 Spring Boot 则是对 Java 应用的生命周期的管理。
-
在 Spring 的年代,我们都是使用 Tomcat 外部容器来实现 Java 应用的运行,Spring 只是其中的一个组件。
-
在 Spring Boot 的年代,我们使用 Spring Boot 来管理 Java 应用的运行,内嵌的 Tomcat 反而成为其中的一个组件。
总结
springboot的自动配置的原理,依赖于一下几点:
- 类上添加
@Configuration
注解,声明为配置类 - 方法上添加
@Bean
注解,声明该方法创建一个 Spring Bean - 类上添加
@EnableConfigurationProperties(YunaiServerProperties.class)
使对YunaiServerProperties
定义的属性能在yml文件中使用 @ConditionalOnClass(HttpServer.class)
表示当类路径下有指定类的条件下,才进行装配,类似于由前提,避免运行时产生ClassNotFound
一个自定义的配置类的自动配置的代码设计如下:
@Configuration // 声明配置类
@EnableConfigurationProperties(YunaiServerProperties.class) // 使 YunaiServerProperties 配置属性类生效
public class YunaiServerAutoConfiguration {
private Logger logger = LoggerFactory.getLogger(YunaiServerAutoConfiguration.class);
@Bean // 声明创建 Bean
@ConditionalOnClass(HttpServer.class) // 需要项目中存在 com.sun.net.httpserver.HttpServer 类。该类为 JDK 自带,所以一定成立。
public HttpServer httpServer(YunaiServerProperties serverProperties) throws IOException {
// 创建 HttpServer 对象,并启动
HttpServer server = HttpServer.create(new InetSocketAddress(serverProperties.getPort()), 0);
server.start();
logger.info("[httpServer][启动服务器成功,端口为:{}]", serverProperties.getPort());
// 返回
return server;
}
}
YunaiServerProperties对象如下:
@ConfigurationProperties(prefix = "yunai.server")
public class YunaiServerProperties {
/**
* 默认端口
*/
private static final Integer DEFAULT_PORT = 8000;
/**
* 端口
*/
private Integer port = DEFAULT_PORT;
public static Integer getDefaultPort() {
return DEFAULT_PORT;
}
public Integer getPort() {
return port;
}
public YunaiServerProperties setPort(Integer port) {
this.port = port;
return this;
}
}
使用时仅需在application.yml中使用YunaiServerProperties中定义的属性即可:
yunai:
server:
port: 8888