在向应用程序加入Spring Boot时,有个名为spring-boot-autoconfigure的JAR文件,其中包含很多的配置类。所有的配置如此的与众不同,原因是它们利用了Spring的条件化配置。条件化配置允许配置存在于应用程序中,但在满足某些特定条件之前都忽略这个配置。
举个例子:
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
...
}
以上是Spring Boot自动配置里的DataSourceAutoConfiguration的一个片段。DataSourceAutoConfiguration添加了@Configuration注解,表明这是一个配置类。最重要的是该类添加了@ConditionalOnClass注解,要求Classpath里必须要有DataSource和EmbeddedDatabaseType。如果他们不存在,条件就不成立,DataSourceAutoConfiguration提供的配置就会被忽略掉。
自动配置中使用的条件化注解:
- @ConditionalOnBean: 配置了某个特定Bean
- @ConditionalOnMissingBean: 没有配置特定的Bean
- @ConditionalOnClass: Classpath里有指定的类
- @ConditionalOnMissingClass: Classpath里缺少指定的类
- @ConditionalOnExpression: 给定的SpEL表达式计算结果为true
- @ConditionalOnJava :Java的版本匹配特定值或者一个范围值
- @ConditionalOnJndi: 参数中给定的JNDI位置必须存在一个,如果没有参数,则需要JNDI InitialContext
- @ConditionalOnProperty: 指定的配置属性要有一个明确的值
- @ConditionalOnResource: Classpath里有指定的资源
- @ConditionalOnWebApplication: 这是一个Web应用程序
- @ConditionalOnNotWebApplication: 这不是一个Web应用程序
1.覆盖Spring Boot 的自动配置
我们以Spring Security来举例。想要使用Spring Security,首先在pom.xml添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加完依赖后,重构项目,此时该项目就可以运行。但访问url地址时,它会提示你登录。没错,此时只是用的Spring Security默认的配置,默认的配置可能满足不了我们的需求,那么我们可以显式地覆盖Spring Security的自动配置。
覆盖自动配置的显示安全配置:
package com.jasonper.readinglist;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ReaderRepository readerRepository;
/**
* 此configure方法设置了一个自定义的userDetailsService,这个服务可以是任意实现了userDetailsService的类,用于查找指定用户名的用户。
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> readerRepository.findOne(username));
}
/**
* 此configure方法指明:
* "/"的请求只有经过身份认证且具有READER角色的用户采纳访问,其他的所有请求路径向所有的用户开放了访问权限。
* 这里还指定了登录页面和登录失败页面
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").access("hasRole('READER')")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true");
}
}
上面的例子足以演示如何覆盖Spring Boot提供的安全自动配置。想要覆盖Spring Boot的自动配置,我们所要做的仅仅是编写一个显示的java配置而已。
2.通过属性文件外置配置
Spring Boot应用程序有多种设置途径,包括如下几处:
(1)命令行参数
(2)java:comp/env里的JNDI(Java Naming and Directory Interface)
(3)JVM系统属性
(4)操作系统环境变量
(5)随机生成的带random.*前缀的属性
(6)应用程序以外的application.properties或者application.yml文件
(7)打包在应用程序内的application.properties或者application.yml文件
(8)通过@PropertySource标注的属性源
(9)默认属性
Tips:上列表按照优先级排序。
application.properties和application.yml文件能放在以下四个位置:
- 外置,在相对于应用程序运行目录的/config子目录里
- 外置,在应用程序运行的目录里
- 内置,在config包内
- 内置,在Classpath根目录
此外,如果你在同一优先级位置同时有application.properties和application.yml,那么application.yml里的属性会覆盖application.properties里的属性
- 自动配置微调
在Spring Boot里有300多个属性可以用来微调应用程序里的Bean。
例如,通过application.yml来更改嵌入式服务器端口:
server:
port: 8000
- 应用程序的Bean的配置外置
假设我们在application.yml配置了如下信息:
person:
name: 张三
age: 20
想要在某个类上拿到刚刚所配置的信息,那很简单,只需在类上配置如下注解,即可在该用的地方使用刚刚配置的属性:
@ConfigurationProperties(prefix = "person")
当然,我们也可以在一个Bean里加载配置属性:
package com.jasonper.readinglist;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("person") // 从application.yml里的person中获得属性
public class PersonProperties {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
- 使用Profile进行配置
当应用程序需要部署到不同的运行环境时,一些配置细节通常会有所不同。比如数据库连接在开发环境和生产环境下就会不一样,在测试环境下又不一样。此时可以使用Profile。Profile是一种条件化配置,基于运行时激活的Profile,会使用或者忽略不同的Bean或配置类。
以Spring Security为例:
@Profile("production")
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
...
}
这里的@Profile注解要求运行时激活production Profile,这样才能应用该配置。激活配置的方式有很多种,这里使用application.yml文件来激活:
spring:
profiles:
active: production
- 使用特定于Profile的属性文件
如果你正在使用application.properties,可以创建额外的属性文件,遵循application-{profile}.properties这种命名格式,这样就能提供特定于Profile的属性了。
- 使用多Profile YAML文件进行配置
如果使用YAML来配置属性,则可以遵循与配置文件相同的命名规范:application-{profile}.yml这样的YAML文件,并将与Profile无关的属性继续配置在application.yml里。
同时,可以把所有的Profile的配置属性都放在同一个application.yml文件里。使用日志配置来举个例子:
logging:
level:
root: INFO
---
spring:
profiles: dev
logging:
level:
root: DEBUG
---
spring:
profiles: production
logging:
path: /tmp/
file: BookWorm.log
level:
root: WARN
这个application.yml分为三部分,使用一组三个连字符作为分隔符。第二段和第三段分别指定了不同的环境下的日志配置。第二段是开发环境下的,第三段是生产环境下的。
另一方面,第一段未指定spring.profile,因此这里的属性对全部Profile都生效,或者对那些未设置该属性的激活Profile生效。
3.定制应用程序的错误页面
定制应用程序的错误页面,只需在src/main/resources/templates中加入自己写的错误页面即可。Spring Boot会为错误视图提供如下错误属性:
- timestamp:错误发生的时间
- status:错误状态码
- error:错误的原因
- exception:异常的类型
- message:异常消息(如果这个错误是由异常引起的)
- errors:BindingResult异常里的各种错误(如果这个错误是由异常引起的)
- trace:异常跟踪信息(如果这个错误是由异常引起的)
- path:错误发生时请求的url路径