1. 配置文件
Spring Boot使用一个全局的配置文件,配置文件的名称是固定的。以下两类配置文件都可以被Spring Boot加载
application.propertiesapplication.yml
配置文件的作用:修改Spring Boot自动配置的默认值,Spring Boot会加载配置文件并帮我们自动配置好。
配置文件一般放置在 src/main/resources 目录或者 类路径下/config
2. YAML相关
2.1 YAML简介
YAML:YAML Ain't Markup Language(不是一种标记语言),还有另一种解释Yet Another Markup Language"(仍是一种标记语言)。YAML作为一种标记语言用于书写配置文件,YAML不同于以往的 xxx.xml 书写的配置文件,YAML以数据为中心,比json、xml等更适合作配置文件,书写的配置文件为 xxx.yml 或 xxx.yaml。
YAML与XML书写配置对比
YAML写法
server:
port: 8081
XML写法
<server>
<prot>8081</prot>
</server>
YAML优点:
- YAML易于人们阅读。
- YAML数据在编程语言之间是可移植的。
- YAML匹配敏捷语言的本机数据结构。
- YAML具有一致的模型来支持通用工具。
- YAML支持单程处理。
- YAML具有表现力和可扩展性。
- YAML易于实现和使用。
2.2 YAML语法
2.2.1 基本语法
基本写法:K:(空格)V 表示一个键值对(注意空格)
- 使用缩进表示层级关系
- 缩进时只能使用空格,不能使用Tab缩进
- 缩进的空格数目不重要,只要相同层级元素左对齐即可
- 大小写敏感
- 松散表示,java中对于驼峰命名法,可用原名或使用-代替驼峰。如java中的lastName属性,在yml中使用lastName或 last-name都可正确映射。
2.2.2 值的写法
① 字面量:普通的值(数字,字符串,布尔)
k: v 直接书写,注意空格。
字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格
字符串默认不需要加单引号或双引号,引号有不同的意思,不加引号相当于使用单引号。
-
""双引号:不会转义字符串里的特殊字符,特殊字符会表示原本的意思。例:
name: "zhangsan \\ wangwu"输出:zhangsan \ lisi -
''单引号:会转义字符串里的特殊字符。特殊字符最终只是一个普通的字符串数据。例:
name: 'zhangsan \\ lisi'输出:zhangsan \\ lisi
代码示例:
name: zhangsan
age: 12
birthday: 2020/10/14
② 对象、Map(属性和值)(键值对)
-
对象的一组键值对集合,使用
:分隔。冒号后加空格来分开键值; -
下一行开始写属性和值的对应关系,缩进时要保持左侧对齐;
-
也可以写在同一行,属性用
{}包起来,用,进行属性分割;
代码示例:
person:
name: lisi
age: 23
sex: male
birthday: 2020/10/12
dog:
name: huahua
age: 2
行内写法:
person: {name: lisi, age: 23, sex: flmale, birthday: 2020/10/12}, dog: {name: huahua, age: 2}
③ 数组(List、Set)
用 - 表示数组中的一个元素,一组以 — 开头的行构成一个数组;
也可以使用行内写法,行内写法用 [] 包起来;
代码示例:
list:
-aaa
-bbb
-ccc
行内写法
list:[aaa, bbb, ccc]
④ 文档
多个文档之间用 --- 分隔
代码示例:
server:
port: 8081
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dev
---
server:
port:8084
spring:
profiles: prod
YAML语法检验测试地址:YAML语法检验
3. 配置文件值注入
3.1 属性注入
两种方式通过 @Value 注解和 @ConfigurationProperties 注解,均可以从配置文件中获取到值。
@ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
@Value:为属性注入一个值;
两种方式获取值比较
@ConfigurationProperties | @Value | |
|---|---|---|
| 功能 | 批量注入配置文件中的属性 | 单个指定 |
| 松散绑定(松散语法) | 支持 | 不支持 |
| SpEL | 不支持 | 支持 |
| JSR303数据校验 | 支持 | 不支持 |
| 复杂类型封装 | 支持 | 不支持 |
无论配置文件时yml还是properties都可以获取到值;
当我们某个业务逻辑只是需要获取配置文件中的某项值,则使用
@Value;当我们编写了一个javaBean和配置文件进行映射时,需要批量注入属性时,使用
@ConfigurationProperties
使用 @Valiadated 注解对javabean进行配置文件注入数据校验:
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
// @Value("${person.last-name}")
@Email(message = "邮箱验证失败")
private String lastName;
// @Value("#{11*2}")
private Integer age;
private Boolean boss;
private Date birthday;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
private Map<String, Object> mapsInline;
private List<Object> listsInline;
}
关于
@Validatede注解的使用,此处不进行详细的介绍,会在后续的博客中进行详细的介绍。
3.2 加载配置文件
@PropertySource 和 @ImportResource 注解可以用来加载配置文件。
① @PropertySource :加载指定的配置文件。
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中person下面的所有属性和类中的属性进行一一映射
*
* 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
* @ConfigurationProperties(prefix = "person")默认从全局配置文件中获取值;
*
*/
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
//@Validated
public class Person {
// @Value("${person.last-name}")
// @Email(message = "邮箱验证失败")
private String lastName;
// @Value("#{11*2}")
private Integer age;
private Boolean boss;
private Date birthday;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
private Map<String, Object> mapsInline;
private List<Object> listsInline;
}
② @ImportResource:导入Spring的配置文件,让Spring配置文件里的内容生效。
- Spring Boot项目中没有Spring配置文件,我们编写的Spring配置文件默认是不会加载的;
- 如果想让Spring配置文件生效,需要使用
@ImportSource注解,标注在主启动类上;
外部Spring配置文件beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="cn.bruce.springboot.service.HelloService"></bean>
<bean id="test" class="cn.bruce.springboot.service.TestService"/>
</beans>
主启动类SpringBootApplication
@ImportResource(locations = {"classpath:beans.xml"})
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
Spring官方并不建议导入外部Spring配置文件,建议使用配置类来进行Spring配置,即使用全注解配置
@Configuration:指明当前类是一个配置类。用于替代以前的Spring配置文件
@Configuration
public class MyAppConfig {
/**
* @Bean相当于在配置文件中用<bean><bean/>标签,用于注入组件
*/
@Bean
public HelloService helloService() {
System.out.println("配置类@Bean给容器添加组件。。。。");
return new HelloService();
}
}
3.3 配置文件占位符
可以在配置文件中使用文件占位符 ${xxxx} 使用随机数或引用属性也可以设置默认值
3.3.1 随机数
使用以下占位符可以生成随机数,还可以指定随机数类型
${random.uuid} :随机生成uuid
${random.value} :随机生成字符串
${random.int} :随机生成int型数字
${random.int(value,[max])} 随机生成数字,指定最大值
${random.long}:随机生成log型数字
${random.long(value,[max])}:随机生成long型数字,可指定最大值
3.3.2 属性占位符
- 可以在配置文件中引用以前配置过的属性;
- 使用
:来指定默认值;
代码示例:
person.last-name=张三${random.value}
person.age=${random.int(100)}
person.birthday=2020/10/11
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.last-name} have a dog
person.dog.age=${initAge:12}
4. 关于Profile
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速进行环境切换。
4.1 多Profile文件形式
在编写主配置文件时,文件名可以是 application-{profile}.properties/yml。如:application-dev.properties 、 application-prod.properties
默认使用 application.properties 里的配置;在 application.properties 文件中激活指定的profile
4.2 多Profile文档块形式
在YAML中使用 --- 来划分文档块,可以使用不同的文档块来配置不同的profile,在默认配置中激活指定的profile
#默认配置
server:
port: 8081
spring:
profiles:
active: dev
---
#开发环境
server:
port: 8083
spring:
profiles: dev
---
#生产环境
server:
port: 8084
spring:
profiles: prod
4.3 激活指定的Profile
① 在配置文件 application.properties/yml 中指定 spring.profiles.active={profile}}
② 在命令行指定,配置命令 --spring.profiles.active={profile}} 在运行构建好的jar包时添加配置命令
③ 添加虚拟机参数 -Dspring.profiles.active=dev
在IDE中配置命令行参数和虚拟机参数,以IDEA为例:
注意:{profile} 表示环境名称,如:dev、prod等
5. 配置文件的加载
5.1 配置文件加载位置
Spring Boot在启动时会扫描以下位置的 application.properties 和 application.yml 文件作为Spring Boot的默认配置文件。
file:./config/:项目中config文件夹内file:./:项目文件内classpath:/config/:src/resource/config文件夹内classpath:/:src/resource文件夹内
加载的优先级由高到低,高优先级的配置会覆盖低优先级的配置;
Spring Boot会加载这四个位置的全部配置文件,形成互补配置;
可以通过 spring.config.location 改变默认配置文件位置;
可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;便于运维时调整项目的配置信息,可以通过命令行调整配置信息。
命令行参数: --spring.config.location=G:/application.properties
如在部署时执行此命令:java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=E:/application.properties
5.2 外部配置文件加载顺序
Spring Boot支持多种配置方式,不仅可以在项目内部加载信息,也可以加载外部的配置信息。Spring Boot会加载以下位置的信息,优先级由高到低。高优先级的配置会覆盖低优先级的配置,所有的配置会形成互补配置。
① 命令行参数。所有的配置都可以在命令行中指定
例如:java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
② 来自 java:comp/env 的JNDI属性;
③ Java的系统属性 System.getProperties();
④ 操作系统环境;
⑤ RandomValuePropertySource 配置的 random.* 属性值;
⑥ jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件;
⑦ jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件;
⑧ jar包外部的application.properties或application.yml(不带spring.profile)配置文件;
⑨ jar包内部的application.properties或application.yml(不带spring.profile)配置文件;
⑩ @Configuration 注解类上的 @PropertySource;
通过 SpringApplication.setDefaultProperties 指定的默认属性;
Spring Boot官方文档: Spring Boot加载顺序
总结:加载jar包内外部配置文件的顺序,由jar包外向jar包内寻找,优先加载带
{profile}的配置文件,再加载不带{profile}的配置文件。即:jar包配置由外到内,由带{profile}的到不带{profile}的
6. 自动配置的原理
Spring Boot中的配置文件能书写那些内容,以及书写的规则由它的自动配置原理里来决定,因此理解了Spring Boot的自动配置原理也就掌握了Spring Boot配置文件的内容分。
6.1 自动配置原理
Spring Boot在启动的时候会加载主配置类,主配置类中包含 @SpringBootApplication 注解,此注解是个组合注解,其中包含Spring Boot开启自动配置功能的注解 @EnableAutoConfiguration ,以下内容均从此注解的构成和功能展开。
@EnableAutoConfiguration 注解的作用:提示Spring Boot开启自动配置,并加载配置信息。配置信息由Spring Boot自动配置信息和我们书写的配置文件信息共同构成。
6.1.1 @EnableAutoConfiguration 注解加载自动配置类的过程:
@EnableAutoConfiguration 注解源码
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* 指定配置文件中的配置项
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
源码可知
@EnableAutoConfiguration注解由@AutoConfigurationPackage和@Import注解构成。
@AutoConfigurationPackage注解:自动配置包注解
@Import注解:是Spring的一个底层注解用于给容器注入一个组件。
@Import(AutoConfigurationImportSelector.class):导入AutoConfigurationImportSelector类配置的自动配置信息。
① AutoConfigurationImportSelector 类实现了 ImportSelector 接口中的 selectImport 方法,用于选择导入的自动配置;而 selectImport 方法中调用 getAutoConfigurationEntry 方法;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 调用此方法进行配置信息加载
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
② getAutoConfigurationEntry 方法中调用 getCandidateConfigurations 方法;
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);
}
③ getCandidateConfigurations 方法中调用 SpringFactoriesLoader 类中的 loadFactoryNames 方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
调用
loadFactoryNames方法时传入getSpringFactoriesLoaderFactoryClass()作为参数,此方法返回EnableAutoConfiguration注解,即加载EnableAutoConfiguration注解中规定的的默认配置文件信息。protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
④ SpringFactoriesLoader 类中包含执行加载外部配置文件信息的方法
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
* 加载的META-INF/spring.factories路径中获取EnableAutoConfiguration指定的值,
* 将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
private SpringFactoriesLoader() {
}
/**
* Load and instantiate the factory implementations of the given type from
* {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.
* <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}.
* <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}
* to obtain all registered factory names.
* @param factoryType the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
* @throws IllegalArgumentException if any factory implementation class cannot
* be loaded or if an error occurs while instantiating any factory
* @see #loadFactoryNames
*/
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "'factoryType' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
/**
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
* class loader.
* @param factoryType the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading resources; can be
* {@code null} to use the default
* @throws IllegalArgumentException if an error occurs while loading factory names
* @see #loadFactories
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
/**
* 是整个流程中最终执行动作的方法,此方法使用类加载器加载外部的配置文件,
* 将配置文件中的配置进行读取并使用LinkedHashMap作为存储结构,
* 最终返回给配置信息的信息。
*
*/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}
}
loadSpringFactories 方法的主要功能:
- 扫描所有jar包类路径下
META‐INF/spring.factories; - 把扫描到的这些文件的内容包装成properties对象;
- 从properties中获取到
EnableAutoConfiguration.class类对应的值,然后把他们添加在容器中;
经过一系列的方法调用,最终Spring Boot得到了配置文件中的自动配置类相关的信息。通过
@Import注解将AutoConfigurationImportSelector加载到到IoC容器中,通过调用ImportSelector接口的selectImports方法,就可以拿到配置文件中的配置信息,完成Spring Boot的自动配置。
Spring Boot自动配置文件由 SpringFactoriesLoader 类规定路径和文件,@EnableAutoConfiguration 注解规定配置文件中配置中的值,最终指向了自动配置的信息。
Spring Boot自动配置信息位置:spring-boot-autoconfigure-2.3.4.RELEASE.jar!\META-INF\spring.factories 以下这些配置信息就是Spring Boot自动配置的类。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
.......,\
.......,\
.......,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
每一个这样的
xxxAutoConfiguration类都是容器中的一个组件,都会被加载到容器中。Spring Boot通过这些配置类用来做自动配置;每一个自动配置类完成某种功能的自动配置工作;
6.1.2 自动配置类加载配置的原理
自动配置类中完成对相关功能的配置工作,这里是自动配置执行的最后一步
以 HttpEncodingAutoConfiguration 类(Http编码自动配置类)为例解释配置类自动配置原理。
自动配置类:HttpEncodingAutoConfiguration
@Configuration(proxyBeanMethods = false)// 指明此类是个配置类,可以为容器中注入组件
@EnableConfigurationProperties(ServerProperties.class)// 启动指定类的ConfigurationProperties功能;将配置文件中对应的值和ServerProperties绑定起来;并把ServerProperties加入到IoC容器中
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)// 判断当前应用是否为web应用,如果是则配置类生效。
//ConditionOnXXX注解是Spring底层注解@Conditional注解的扩展,配置如果满足指定条件则配置生效,否则整个配置不生效。
@ConditionalOnClass(CharacterEncodingFilter.class)// 判断当前项目中是否有CharacterEncodingFilter类,此类是SpringMVC解决乱码问题的过滤器
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) // 判断配置文件中是否存在某项配置 server.servlet.encoding ;即我们的配置文件中是否配置了server.servlet.encoding项;如果没有配置默认此项配置也是生效的;
public class HttpEncodingAutoConfiguration {
// 与SpringBoot配置文件进行映射
private final Encoding properties;
// 在只有一个有参构造器的情况下,参数值会从容器中获取
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@Bean // 向容器中添加一个组件,这个组件中的参数值需要从properties中
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}
所有的配置文件中能配置的属性都是在 xxxProperties 属性类中封装;配置文件能配置那些内容可以参考某个自动配置类的属性类。如配置类 HttpEncodingAutoConfiguration 中的属性配置类 ServerProperties
自动配置属性类:ServceProperties
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)// //从配置文件中获取指定的值和bean的属性进行绑定
public class ServerProperties {
// 如此处设置uri的默认编码为UTF-8,同样也可以在配置文件中进行指定
private Charset uriEncoding = StandardCharsets.UTF_8;
......
}
ConditionOnXXX 注解是Spring底层注解 @Conditional 注解的扩展,用于判断当前配置类是否满足某些条件,如果满足则配置类生效。配置类如果生效则向IoC容器中注入各种组件,而这些组件的属性是从与之相对应的 xxxProperties 类中获取的,而 xxxProperties 配置类中的每个属性也是和配置文件中的配置项相对应的。
关于各种类和注解说明:
@ConditionOnXXX:判断当前配置类是否满足条件;xxxAutoConfiguration:自动配置类;用于给容器中注入组件xxxProperties:与自动配置类相对应的属性类;封装配置文件中的相关属性yml/properties:文件中的配置项和属性类中的值相对应;
Spring Boot自动配置总结:
① Spring Boot中包含大量的自动配置类
xxxAutoConfiguration;② Spring Boot在启动的时候会加载自动配置类;
③ 当配置类中的约束条件满足时,自动配置类会加载到IoC容器中;
④ 自动配置类中的配置属性类
xxxProperties中设置了可以配置的属性;⑤ Spring Boot会将配置属性类中的属性和配置文件中的数据项进行映射;
对我们使用Spring Boot的启示:
① Spring Boot在启动时会加载大量的自动配置;
② 在使用Spring Boot时我们可以查看我们所需的功能Spring Boot有没有相关的自动配置类;
③ 如果有则查看此自动配置类配置了那些组件;(如果我们需要的组件已经加载,则不需要进行配置)
④ 在给容器中自动配置添加组件时,会从自动配置的属性类中加载某些属性,我们可以在配置文件中指定这些属性的值;
6.2 @Conditional 派生注解
ConditionOnXXX 注解是Spring底层注解 @Conditional 注解的派生注解,用于判断当前配置类是否满足某些条件,如果满足则配置类生效。
作用:只有 @Conditional 派生注解指定的条件成立,才会给容器中添加组件,配置类里面的所有内容才生效;
@Conditional 派生注解 | 作用(判断是否满足指定条件) |
|---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
自动配置类在一定的条件下才能生效,我们可以通过在配置文件中配置打开debug属性 debug=true ,即可查看被Spring Boot加载的配置类,这样就不需要我们逐个查看各个自动配置类的加载条件。
控制台打印的信息
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:// 加载的自动配置类
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
AopAutoConfiguration.ClassProxyingConfiguration matched:
- @ConditionalOnMissingClass did not find unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition)
- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)
......
Negative matches:// 未加载的自动配置类
-----------------
ActiveMQAutoConfiguration:
Did not match:// 未加载的原因
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
......