本章内容
HelloBookController 控制层中,在代码中以硬编码的方式使用字符串表示书信息。下面把书的信息作为属性,外化配置在 application.properties 。好处是将应用参数、业务参数或第三方参数等统一配置在应用配置文件中,避免配置侵入业务代码,达到可配置的方式,方便及时调整修改。## 书信息
demo.book.name=[Spring Boot 2.x Core Action]
demo.book.writer=BYSocket
## 书信息
demo:
book:
name: 《Spring Boot 2.x 核心技术实战 - 上 基础篇》
writer: 泥瓦匠BYSocket
demo.book.writer=泥瓦匠 应该配置成 demo.book.writer=\u6ce5\u74e6\u5320 。利用 IDEA properties 插件 或利用 Java 文件转码工具 native2ascii 来快速地进行转义。该工具有在线版实现,地址如下:demo.springboot.config ,并在目录中创建名为 BookProperties 的属性类,代码如下:import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 书属性
*/
@Component
public class BookProperties {/**
* 书名
*/
@Value("${demo.book.name}")
private String name;
/**
* 作者
*/
@Value("${demo.book.writer}")
private String writer;
// ... 省略 getter / setter 方法
}
@Component 注解定义了书的属性 Bean,并通过 @Value 注解为该 Bean 的成员变量(或者方法参数)自动注入 application.properties 文件的属性值。@Value 注解是通过 “${propName}” 的形式引用属性,propName 表示属性名称。@Component 对类进行标注,职责是泛指组件 Bean ,应用启动时会被容器加载并加入容器管理。常见的 @Controller、@Service 、@Repository 是 @Component 的分类细化组件,分别对应控制层、服务层、持久层的 Bean。@Value 对 Bean 的字段或者方法参数进行标注,职责是基于表达式给字段或方法参数设置默认属性值。通常格式是注解 + SpEL 表达式,如 @Value("SpEL 表达式")。@Vlaue 注解来引用属性值时,确保所引用的属性值在 application.properties 文件存在并且相对应匹配,否则会造成 Bean 的创建错误,引发 java.lang.IllegalArgumentException 非法参数异常。HelloBookController 类,通过注入的方式获取书属性 Bean 并返回。代码如下:import demo.springboot.config.BookProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloBookController {@Autowired
BookProperties bookProperties;
@GetMapping("/book/hello")
public String sayHello() {
return "Hello, " + bookProperties.getWriter() + " is writing "
+ bookProperties.getName() + " !";
}
}
@Autowired 注解标记在 BookProperties 字段,控制层自动装配属性 Bean 并使用。默认情况下要求被注解的 Bean 必须存在,需要允许 NULL 值,可以设置其 required 属性为 false: @Autowired(required = false)。ConfigApplication 类启动,在控制台看到成功运行的输出后,打开浏览器访问 /book/hello 地址,可以看到如图 2-2 所示的返回结果:
import demo.springboot.config.BookProperties;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ConfigApplicationTests {
@Autowired
BookProperties bookProperties;
@Test
public void testBookProperties() {
Assert.assertEquals(bookProperties.getName(),"'Spring Boot 2.x Core Action'");
Assert.assertEquals(bookProperties.getWriter(),"BYSocket");
}
}
@Value 和 @ConfigurationProperties 注解两种方式。两种方式适合的场景不同,下面具体介绍其使用方法和场景。@Value 注解对 Bean 的变量或者方法参数进行标注,职责是基于表达式给字段或方法参数设置默认属性值。通常格式是注解 + SpEL 表达式,如 @Value("SpEL 表达式"),并标注在对应的字段或者方法上方,且必须对变量一一标注。这种方式适用于小而不复杂的属性结构。属性结构复杂,字段很多的情况下,这种方式会比较繁琐,应该考虑使用 @ConfigurationProperties 注解。@PropertySource 注解引入对应路径的其他 .properties 文件。将书信息重新配置在 classpath 下新的 book.properties 配置文件后,读取新配置文件的代码如下:import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* 书属性
*/
@Component
@PropertySource("classpath:book.properties")
public class BookProperties {/**
* 书名
*/
@Value("${demo.book.name}")
private String name;
/**
* 作者
*/
@Value("${demo.book.writer}")
private String writer;
// ... 省略 getters / setters 方法
}
demo.springboot.config 中创建名为 BookComponent 的属性类,并使用 @ConfigurationProperties 注解获取属性,代码如下:import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 书属性
*
*/
@Component
@ConfigurationProperties(prefix = "demo.book")
public class BookComponent {/**
* 书名
*/
private String name;
/**
* 作者
*/
private String writer;
// ... 省略 getters / setters 方法
}
@Value 注解方式,使用 @ConfigurationProperties(prefix = "demo.book") 注解标注在类上方可以达到相同的效果。 @ConfigurationProperties 注解的 prefix 是指定属性的参数名称。会匹配到配置文件中 “ demo.book.* ” 结构的属性,星号 “ * ” 是指会一一对应匹配 BookComponent 类的字段名。例如,字段 name 表示书名,会匹配到 demo.book.name 属性值。@Value 注解方式强制字段必须对应在配置文件, @ConfigurationProperties 注解方式则不是必须的。一般情况下,所有字段应该保证一一对应在配置文件。如果没有属性值对应的话,该字段默认为空, @ConfigurationProperties 注解方式也不会引发任何异常,Spring Boot 推荐使用 @ConfigurationProperties 注解方式获取属性。@Autowired
BookComponent bookComponent;
@Test
public void testBookComponent() {
Assert.assertEquals(bookComponent.getName(),"'Spring Boot 2.x Core Action'");
Assert.assertEquals(bookComponent.getWriter(),"BYSocket");
}
@ConfigurationProperties 注解方式支持验证功能,即当属性类被 @Validated 注解标注时,Spring Boot 初始化时会验证类的字段。在类的字段上添加 JSR-303 约束注解,进行数据验证。下面为书属性字段添加非 NULL 和字符串非空约束,代码如下:import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 书属性
*
*/
@Component
@ConfigurationProperties(prefix = "demo.book")
@Validated
public class BookComponent {/**
* 书名
*/
@NotEmpty
private String name;
/**
* 作者
*/
@NotNull
private String writer;
// ... 省略 getters / setters 方法
}
@Validated 注解开启对 BookComponent 类字段的数据验证,如果 name 字段为 NULL 或者为空字符串时,会引发 BindValidationException 绑定数据验证异常。数据验证常用在邮箱格式或者有长度限制的属性字段。另外,验证嵌套属性的值,必须在嵌套对象字段上方标注 @Valid 注解,用来触发其验证。例如,在书属性中新增嵌套对象出版社 Publishing,就需要在该对象上方标注 @Valid 注解,来开启对 Publishing 对象的数据验证。综上,两种属性获取方式各有优缺点,对比如图 2-3 所示:
// chapter-2-spring-boot-config 目录下运行
java -jar target/chapter-2-spring-boot-config-1.0.jar
java -jar target/chapter-2-spring-boot-config-1.0.jar --demo.book.writer=Jeff
setAddCommandLineProperties 方法为 false ,用于关闭命令行配置功能,代码如下:SpringApplication.setAddCommandLineProperties(false);
@TestPropertySource 注解配置@SpringBootTest 注解的 properties 配置ServletConfig 初始化参数配置ServletContext 初始化参数配置RandomValuePropertySource 配置@Configuration 类中的 @PropertySource 注解配置SpringApplication.setDefaultProperties 指定)## 书信息
demo.book.name=[Spring Boot 2.x Core Action]
demo.book.writer=BYSocket
demo.book.description=${demo.book.writer}'s${demo.book.name}
RandomValuePropertySource 类随机提供整形、长整形数、UUID 或者字符串。使用代码如下:my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
spring.profiles.active 命令去指定读取特定配置文件的属性。多环境配置文件是不同于 application.properties 应用配置文件。多环境配置文件的约定命名格式为 application-{profile}.properties。多环境配置功能默认为激活状态,如果其他配置未被激活,则 {profile} 默认为 default,会加载 application-default.properties 默认配置文件,没有该文件就会加载 application.properties 应用配置文件。## 书信息
demo.book.name=[Spring Boot 2.x Core Action]From Dev
demo.book.writer=BYSocket
## 书信息
demo.book.name= From Prod
demo.book.writer=BYSocket
java -jar target/chapter-2-spring-boot-config-1.0.jar --spring.profiles.active=dev
--spring.profiles.active=dev 指定读取某个配置文件,将 dev 更改成 prod ,轻松切换读取生产环境配置。也可以在控制台的日志中确定配置读取来自 dev :2017-11-09 12:10:52.978INFO 72450 --- [main] demo.springboot.ConfigApplication: The following profiles are active: dev
spring-boot-autoconfigure 依赖实现了默认的配置项,即应用默认值。这种模式叫做 “自动配置”。Spring Boot 自动配置会根据添加的依赖,自动加载依赖相关的配置属性并启动依赖。例如默认用的内嵌式容器是 Tomcat ,端口默认设置为 8080。spring-boot-autoconfigure 依赖中,包括 Spring MVC 、Data 和其它框架的自动配置。spring-boot-autoconfigure 依赖,是 Spring Boot 实现自动配置的核心 Starter 组件。其实现源码包结构如图 2-6 所示:
org.springframework.boot.autoconfigure
org.springframework.boot.autoconfigure.data.jpa
org.springframework.boot.autoconfigure.thymeleaf
org.springframework.boot.autoconfigure.web.servlet
org.springframework.boot.autoconfigure.web.reactive
... 省略
JpaRepositoriesAutoConfiguration
ThymeleafAutoConfiguration
WebMvcAutoConfiguration
WebFluxAutoConfiguration
... 省略
spring-boot-autoconfigure 职责是通过 @EnableAutoConfiguration 核心注解,扫描 ClassPath 目录中自动配置类对应依赖,并按一定规则获取默认配置并自动初始化所需要的 Bean。在 application.properties 配置文件也可以修改默认配置项,常用配置清单地址如下:@EnableAutoConfiguration 注解中 @Import 的 AutoConfigurationImportSelector 自动配置导入选择器类实现的。查阅源码可得具体流程如下:AutoConfigurationImportSelector 通过 SpringFactoriesLoader.loadFactoryNames() 核心方法读取 ClassPath 目录下面的 META-INF/spring.factories 文件。WebMvcAutoConfiguration Web MVC 自动配置类和ServletWebServerFactoryAutoConfiguration 容器自动配置类 。spring-boot-starter-web 依赖后,启动应用会触发容器自动配置类。容器自动配置类 ServletWebServerFactoryAutoConfiguration 的部分代码如下:package org.springframework.boot.autoconfigure.web.servlet;
@Configuration
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class ServletWebServerFactoryAutoConfiguration {
... 省略
}@ConditionalOnClass 注解表示对应的 ServletRequest 类在 ClassPath 目录下面存在,并且 @ConditionalOnWebApplication 注解表示该应用是 Servlet Web 应用时,才会去启动容器自动配置,并通过 ServerProperties 类默认设置了端口为 8080。Type.SERVLET 枚举代表 Servlet Web 应用,Type.REACTIVE 枚举代表响应式 WebFlux 应用。@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAutoConfiguration 注解,代码如下:@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
org.springframework.boot
spring-boot-starter
true
org.springframework.boot
spring-boot-configuration-processor
true
io.springfox
springfox-swagger-ui
${version.swagger}
io.springfox
springfox-swagger2
${version.swagger}
io.springfox
springfox-bean-validators
${version.swagger}
org.projectlombok
lombok
1.16.12
provided
spring-boot-starter 组件依赖用于自动配置特性,springfox-swagger2 依赖是 Swagger2 框架。SwaggerProperties Swagger2 属性配置类,包含了所有默认属性值。使用该组件时,可以在 application.properties 配置文件配置对应属性项,进行覆盖默认配置。代码如下:import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import springfox.documentation.schema.ModelRef;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {/**是否开启swagger**/
private Boolean enabled;
/**标题**/
private String title = "";
/**描述**/
private String description = "";
/**版本**/
private String version = "";
/**许可证**/
private String license = "";
/**许可证URL**/
private String licenseUrl = "";
/**服务条款URL**/
private String termsOfServiceUrl = "";
private Contact contact = new Contact();
/**swagger会解析的包路径**/
private String basePackage = "";
/**swagger会解析的url规则**/
private List basePath = new ArrayList<>();
/**在basePath基础上需要排除的url规则**/
private List excludePath = new ArrayList<>();
/**分组文档**/
private Map docket = new LinkedHashMap<>();
/**host信息**/
private String host = "";
/**全局参数配置**/
private List globalOperationParameters;
... 省略,具体代码见 GitHub
}
@ConfigurationProperties(prefix = "swagger") 标注在类上方是指定属性的参数名称为 swagger。会对应匹配到配置文件中 “ swagger.* ” 结构的属性,例如,字段标题 title 表示标题,会匹配到 swagger.title 属性值。SwaggerAutoConfiguration Swagger2 自动配置类,提供 Swagger2 依赖关系管理功能和自动配置功能。代码如下:import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Configuration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
@Import({
Swagger2DocumentationConfiguration.class,
BeanValidatorPluginsConfiguration.class
})
public class SwaggerAutoConfiguration implements BeanFactoryAware {private BeanFactory beanFactory;
@Bean
@ConditionalOnMissingBean
public SwaggerProperties swaggerProperties() {
return new SwaggerProperties();
}@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public List createRestApi(SwaggerProperties swaggerProperties) {
... 省略,具体代码见 GitHub
}@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
@Configuration 注解标注在类上方,表明该类为配置类。@Import 注解引入 Swagger2 提供的配置类 Swagger2DocumentationConfiguration 和 Bean 数据验证插件配置类 BeanValidatorPluginsConfiguration。@ConditionalOnMissingBean 注解标注了两处方法,当 Bean 没有被创建时会执行被标注的初始化方法。第一处被标记方法是 swaggerProperties() ,用来实例化属性配置类 SwaggerProperties;
第二处被标记方法是 createRestApi(), 用来实例化 Swagger2 API 映射的 Docket 列表对象。@ConditionalOnProperty 注解标注在 createRestApi() 方法,name 属性会去检查环境配置项 swagger.enabled 。默认情况下,属性存在且不是 false 的情况下,会触发该初始化方法。matchIfMissing 属性默认值为 false ,这里设置为 true,表示如果环境配置项没被设置,也会触发。EnableSwagger2Doc Swagger2 启动注解类,用于开关 spring-boot-starter-swagger 组件依赖。代码如下:@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({SwaggerAutoConfiguration.class})
public @interface EnableSwagger2Doc {}
@Import 注解引入 Swagger2 自动配置类 SwaggerAutoConfiguration。当将该注解配置在应用启动类上方,即可开启 Swagger2 自动配置及其功能。
com.spring4all
spring-boot-starter-swagger
2.0
EnableSwagger2Doc,代码如下:import com.spring4all.swagger.EnableSwagger2Doc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableSwagger2Doc // 开启 Swagger
@SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}ConfigApplication 类启动,在控制台看到成功运行的输出后,打开浏览器访问 localhost:8080/swagger-ui.html 地址,可以看到自动生成的 Swagger API 文档,如图 2-7 所示:
【Spring|Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件】本文由博客群发一文多发等运营工具平台 OpenWrite 发布