1. Spring Boot的默认配置

Spring Boot使用“习惯优于配置”(项目中存在大量的配置,此外还内置了一个习惯性的配置,让你无需手动进行配置)的理念让你的项目快速运行起来。所以,我们要想把Spring Boot玩的溜,就要懂得如何开启各个功能模块的默认配置,这就需要了解Spring Boot的配置文件application.propertiesSpring Boot默认配置文件为CLASSPATH(resources目录)下的application.properties(yml)

Spring Boot使用了一个全局的配置文件application.properties,可以放在src/main/resources(yml)目录下或者类路径的/config下,总之打包后需要在CLASSPATH下。

Sping Boot的全局配置文件的作用是对一些默认配置的配置值进行修改。

:如果你工程没有这个application.properties(yml),那就在src/main/java/resources目录下新建一个File,命名为application.properties(yml)

1.1 配置文件

SpringBoot使用一个全局的配置文件,配置文件名是固定的;

  • application.properties
  • application.yml

配置文件的作用 : 修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;

  • YAML(YAML Ain’t Markup Language);
  • YAML A Markup Language:是一个标记语言;
  • YAML isn’t Markup Language:不是一个标记语言;

标记语言:
以前的配置文件; 大多都使用的是 xxxx.xml文件;YAML : 以数据为中心,比jsonxml等更适合做配置文件;

YAML:配置例子

1
2
server: 
port: 8081

相当于XML:

1
2
3
<server>
<port>8081</port>
</server>

1.2 YAML语法

1.2.1 基本语法

K:(空格)V:表示一对键值对(空格必须有);以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的

1
2
3
server:
port: 8081
path: /hello

属性和值也是大小写敏感;

1.2.2 值的写法

  • 字面量: 普通的值(数字,字符串,布尔)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    K: V:字面直接来写;

    字符串默认不用加上单引号或者双引号;

    " ":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思

    name: "zhangsan \n lisi":输出:zhangsan \n lisi

    ' ':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

    name: 'zhangsan \n lisi';输出:zhangsan 换行 lisi
  • 对象、Map(属性和值)(键值对)
    • K: V:在下一行来写对象的属性和值的关系;注意缩进

对象还是K: V的方式

1
2
3
4
user:
id: 1
username: 张三
age: 21

行内写法:

1
user: {id: 1,username: 张三,age: 21}
  • 数组(List、Set)
    - 值表示数组中的一个元素
    1
    2
    3
    4
    pets:
    - cat
    - dog
    - pig
    行内写法
    1
    pets: [cat,dog,pig]

2. 配置文件值注入

application.properties(yml)提供自定义属性的支持,这样我们就可以把一些常量配置在这里:yml:语法中间有空格

1
2
name: "张三"
age: 21

然后直接在要使用的地方通过注解@Value(value=”${name}”),使用SPEL表达式取数据,值就可以绑定到你想要的属性上面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class HelloController {

@Value("${name}")
private String username;

@Value("${age}")
private Integer age;



@GetMapping("/")
public String hello(){
return username+"===="+age;
}

}

我们启动工程输入http://localhost:8080 就可以看到打印了”张三====21”。

有时候属性太多了,一个个绑定到属性字段上太累,官方提倡绑定一个对象的bean,这里我们建一个User.java类,顶部需要使用注解@ConfigurationProperties(prefix = "user")来指明使用哪个,注: 必须和属性名对应,否则为null

1
2
3
4
user:
id: 1
username: "张三"
age: 21

使用 @ConfigurationProperties 注解在idea会给我们提示,需要导入配置文件处理器。
0

pom导入配置文件处理器,以后编写配置就有提示了:

1
2
3
4
5
6
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

3. @Value获取值和@ConfigurationProperties获取值比较

@Value是以前spring底层的,使用@Value需要一个一个参数指定,@ConfigurationProperties批量注入配置文件中的属性。更多比较如下:

|—|@ConfigurationProperties | @Value|
|—|—|
|功能 | 批量注入配置文件中的属性 | 一个个指定|
|松散绑定(松散语法)| 支持 | 不支持|
|SpEL | 不支持 | 支持|
|JSR303数据校验 | 支持 | 不支持|
|复杂类型封装 | 支持 | 不支持|

总结: 配置文件yml还是properties他们都能获取到值;如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;

4. 配置文件注入值数据校验

类上添加@Validated注解,实现JSR303校验

@ConfigurationProperties支持JSR303数据校验,@Value不支持JSR303数据校验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component   //必须把类加到Spring容器中被管理
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {

/**
* <bean class="Person">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/

//lastName必须是邮箱格式
//@Email
//@Value("${person.last-name}")
private String lastName;
//@Value("#{11*2}")
private Integer age;
//@Value("true")
private Boolean boss;
}

5. @PropertySource&@ImportResource&@Bean

@PropertySource:加载指定的配置文件;

person.properties

1
2
3
person.last-name=hfbin
person.age=23
person.boss=true

Person.java

1
2
3
4
5
6
7
8
9
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

private String lastName;
private Integer age;
private Boolean boss;
}

@ImportResource: 导入Spring的配置文件,让配置文件里面的内容生效;Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@ImportResource(locations = "classpath:beans.xml")
public class Panyucable01Application {

public static void main(String[] args) {
SpringApplication.run(Panyucable01Application.class, args);
}

}

SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式

  1. 配置类 @Configuration ——>Spring配置文件
  2. 使用 @Bean 给容器中添加组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
*
* 在配置文件中用<bean><bean/>标签添加组件
*
*/
@Configuration
public class MyAppConfig {

//将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
@Bean
public Person person(){
System.out.println("配置类@Bean给容器中添加组件了...");
return new Person();
}
}

5.1 获取Spring容器

SpringBoot平台下采用ApplicationContextAware的方式完成ApplicationContext实例的获取,并通过ApplicationContext实例完成对Spring管理的Bean实例手动获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Component
public class BeanUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;

private static final Logger logger = LoggerFactory.getLogger(BeanUtils.class);

/**
* Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContext方法
* @param applicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if(BeanUtils.applicationContext == null) {
BeanUtils.applicationContext = applicationContext;
}
}

//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}

//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}

//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}

//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}

6. 配置文件占位符

6.1 随机数

1
2
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}

6.2 占位符获取之前配置的值,如果没有可以是用:指定默认值

可以在配置文件中引用前面配置过的属性(优先级前面配置过的这里都能用)。

1
2
3
4
5
6
7
8
9
10
11
person.last-name=张三${random.uuid}
person.name=$person.last-name}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
# ${person.hello:默认值} 来指定找不到属性时的默认值
person.dog.age=15

7. Profile

ProfileSpring对不同环境提供不同配置功能的支持,可以通过激活、 指定参数等方式快速切换环境 。

7.1 多Profile文件

我们在主配置文件编写的时候,文件名可以是application-{profile}.properties/yml

比如开发环境:application-dev.properties

生产环境 :application-prod.properties

默认使用application.properties的配置

7.2 yml支持多文档块方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server:
port: 8081
spring:
profiles:
active: prod

---
server:
port: 8083
spring:
profiles: dev


---

server:
port: 8084
spring:
profiles: prod #指定属于哪个环境

7.3 激活指定profile

  1. 在配置文件application.properties中指定 spring.profiles.active=dev

  2. 命令行: 可以直接在测试的时候,配置传入命令:

    1
    java -jar spring-boot-panyucable-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
  3. 虚拟机参数

    1
    -Dspring.profiles.active=dev

7.4 配置文件加载位置

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

1
2
3
4
5
6
7
–file:./config/

–file:./

–classpath:/config/

–classpath:/
  • 优先级由高到底,高优先级的配置会覆盖低优先级的配置;
  • SpringBoot会从这四个位置全部加载主配置文件;互补配置;
  • 我们还可以通过spring.config.location来改变默认的配置文件位置,项目打包好以后,
    我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;
    1
    java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
    这个对运维方面起到很大作用

8. 自动配置原理

配置文件到底能写什么?怎么写?自动配置原;

8.1 常用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server.port=9090 # 服务端口号
server.tomcat.uri-encoding=UTF-8 #以Tomcat为web容器时的字符编码
spring.data.mongodb.uri=mongodb://localhost:27017/mydb #mongodb连接

spring.application.name=customer # 应用名称,一般就是项目名称,这个名称在SpringCloud中比较关键
spring.profiles.active=dev #指定当前的活动配置文件,主要用于多环境多配置文件的应用中
spring.http.encoding.charset=UTF-8 #http请求的字符编码
spring.http.multipart.max-file-size=10MB #设置文件上传时单个文件的大小限制
spring.http.multipart.max-request-size=100MB #设置文件上传时总文件大小限制

spring.thymeleaf.prefix=classpath:/templates/ #配置在使用Thymeleaf做页面模板时的前缀,即页面所在路径
spring.thymeleaf.suffix=.html #设置在使用Thymeleaf做页面模板时的后缀
spring.thymeleaf.cache=false #设置在使用Thymeleaf做页面模板时是否启用缓存

spring.mvc.static-path-pattern=/** #设置静态资源的请求路径
spring.resources.static-locations=classpath:/static/,classpath:/public/ #指定静态资源的路径

##以下是使用MySQL数据库的配置
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect #指定数据库方言
hibernate.show_sql=true #是否显示sql语句
hibernate.hbm2dll.auto=update #设置使用Hibernate的自动建表方式
entitymanager.packagesToScan=com.zslin #设置自动扫描的包前缀

spring.datasource.url=jdbc:mysql://localhost:3306/customer?\
useUnicode=true&characterEncoding=utf-8&useSSL=true&autoReconnect=true #数据库链接
spring.datasource.username=root #数据库用户名
spring.datasource.password=123 #数据库用户对应的密码
spring.datasource.driver-class-name=com.mysql.jdbc.Driver #数据库驱动名称

8.2 自动配置原理

  1. 先看@SpringBootApplication
  2. @SpringBootConfiguration:标记当前类为配置类
  3. @EnableAutoConfiguration:开启自动配置
  4. @ComponentScan:扫描主类所在的同级包以及下级包里的Bean
  5. 关键是@EnableAutoConfiguration
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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 {};
    }
    @AutoConfigurationPackage : 将主配置类(@SpringBootConfiguration标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器中,所以说,默认情况下主配置类包及子包以外的组件,Spring 容器是扫描不到的。

而通过@Import(AutoConfigurationImportSelector.class)导入的配置功能;
AutoConfigurationImportSelector中的方法selectImports()调用getAutoConfigurationEntry()在调用getCandidateConfigurations()loadFactoryNames(),得到待配置的class的类名集合,这个集合就是所有需要进行自动配置的类,而是否自动配置的关键在于META-INF/spring.factories·文件中是否存在该配置信息

loadFactoryNames() 中关键的三步:

  1. 从当前项目的类路径中获取所有 META-INF/spring.factories 这个文件下的信息。
  2. 将上面获取到的信息封装成一个 Map 返回。
  3. 从返回的 Map 中通过刚才传入的 EnableAutoConfiguration.class 参数,获取该 key 下的所有值。

2

总结 :自动配置调用链

1
@SpringBootApplication--->@EnableAutoConfiguration--->@Import(AutoConfigurationImportSelector.class)--->selectImports()--->getAutoConfigurationEntry()--->getCandidateConfigurations()--->loadFactoryNames()

打开,如下图可以看到所有需要配置的类全路径都在文件中,每行一个配置,多个类名逗号分隔,而\表示忽略换行
1

SpringApplicationAdminJmxAutoConfiguration类来看其主要构成部分

1
2
3
4
5
6
7
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class})
//配置完JmxAutoConfiguration后再配置当前类型

@ConditionalOnProperty( prefix = "spring.application.admin", value = {"enabled"}, havingValue = "true", matchIfMissing = false)
// spring.application.admin为前缀,属性为enabled,有值时为true,没有匹配到则为false:以上条件为true则实例化,否则不是实例化
public class SpringApplicationAdminJmxAutoConfiguration

都能看到各种各样的条件判断注解,满足条件时就加载这个Bean并实例化,此类的条件注解是:@ConditionalOnProperty

1
2
3
4
5
6
7
8
9
10
11
12
@ConditionalOnBean:当容器里有指定Bean的条件下
@ConditionalOnClass:当类路径下有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式为true的时候作为判断条件才去实例化
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器里没有指定Bean的情况下
@ConditionalOnMissingClass:当容器里没有指定类的情况下
@ConditionalOnWebApplication:当前项目时Web项目的条件下
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnOnSingleCandidate:当指定Bean在容器中只有一个,或者有多个但是指定首选的Bean

这些注解都组合了@Conditional注解,只是使用了不同的条件组合最后为true时才会去实例化需要实例化的类,否则忽略
这种spring4.X带来的动态组合很容易后期配置,从而避免了硬编码,使配置信息更加灵活多变,同时也避免了不必要的意外异常报错。使用的人只要知道配置的条件即可也不用去阅读源码,方便快捷,这也是sprignboot快捷方式带来的好处

参考HttpEncodingAutoConfiguration配置信息如下

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {}

@Configuration:标明为配置类
@EnableConfigurationProperties(HttpEncodingProperties.class)声明开启属性注入
@ConditionalOnClass(CharacterEncodingFilter.class)当CharacterEncodingFilter在类路径的条件下
@ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true)当spring.http.encoding=enabled的情况下,如果没有设置则默认为true,即条件符合
@ConditionalOnMissingBean当容器中没有这个Bean时新建Bean