两种影响自动配置的方式:
使用显式配置进行覆盖和使用属性进行精细化配置
覆盖Spring Boot自动配置
一般,若不用配置就能得到和显式配置一样的结果,那不写配置是最直接的选择。
大多数情况下,自动配置Bean刚好能满足需求,不需要去覆盖他们。但某些情况下,Spring Boot在自动配置时还不能很好地进行推断。
接下来以Spring Security为例,探讨自动配置提供了什么,如何进行覆盖
保护应用程序
首先,在Spring Boot中引入Security起步依赖。以Gradle为例,应添加如下依赖:
compile("org.springframework.boot:spring-boot-starter-security")
相应的,使用Maven ,则需添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Security起步依赖在应用程序的Classpath里添加了Spring Security(和其他东西)。有了这些配置后,自动配置就能介入其中创建一个基本的Spring Security配置。
在浏览器中打开该应用程序,会看到HTTP基础身份验证对话框,用户名为user,密码时在应用程序每次运行时随机生成后写入日志的(控制台输出),如:
Using default security password: d9d8abe5-42b5-4f20-a32a-76ee3df658d9
显然,这个安全配置很粗糙,且一般不会有人想使用这种安全验证方式。那么,如何写出Spring Security配置,覆盖自动配置的安全设置呢?
创建自定义的安全配置
覆盖自动配置只需显式写出配置即可,Spring支持XML和Groovy形式的配置。
编写显式配置时,我们集中来看Java形式如何配置。在Spring Security的场景下,这意味这写一个扩展了WebSecurityConfigureAdapter的配置类。
package 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 SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ReaderRepository readerRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").access("hasRole('READER')")//要求登录者有READER角色
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")//设置登录表单路径
.failureUrl("/login?error=true");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {//定义自定义UserDetailsService
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return readerRepository.findOne(username);
}
});
}
}
SecurityConfig是个非常基础的Spring Security配置,通过此自定义安全配置类,可让Spring Boot跳过安全自动配置,转而使用我们的安全配置。
扩展WebSecurityConfigureAdapter的配置类可以覆盖两个不同的configure()方法。在SecurityConfig里,第一个configure()方法指明,"/"的请求只有经过身份认证且拥有READER角色的用户才能访问。其他的所有请求路径向所有用户开放访问权限。这里还将登录页和失败页(带有一个error属性)指定到了/login
Spring Security为身份认证提供了众多选项,后端可以是JDBC、LDAP和内存用户存储。在这个应用程序中,我们会通过JPA用数据库存储用户信息。第二个configure()方法设置了一个自定义的UserDetailsService,这个服务可以是任意实现了UserDetailsService的类,用于查找指定用户名的用户,简单调用了注入ReaderRepository(这是一个Spring Data JPA仓库接口)的findOne()方法。
package readinglist;
import org.springframework.data.jpa.repository.JpaRepository;
//通过JPA持久化读者
public interface ReaderRepository extends JpaRepository<Reader, String> {
}
用户无需实现ReaderRepository。它扩展了JpaRepository,Spring Data JPA会在运行时自动创建它的实现类,且为用户提供18个操作Reader的视图方法。
Reader类:
package readinglist;
import java.util.Arrays;
import java.util.Collection;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Entity
public class Reader implements UserDetails {
private static final long serialVersionUID = 1L;
@Id
private String username;
private String fullname;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// UserDetails methods
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {//授予READER权限
return Arrays.asList(new SimpleGrantedAuthority("READER"));
}
//不过期,不加锁,不禁用
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Reader用了@Entity注解,所以这是一个JPA实体。此外,它的username字段上有@Id注解,表面这是实体的ID。
Reader实现了UserDetails接口及其中的方法,这样Reader就能代表Spring Security里的用户了,getAuthorities()方法被覆盖过了,始终会为用户授予READER权限。isAccountNonExpired() 、 isAccountNonLocked() 、 isCredentialsNonExpired()
和 isEnabled() 方法都返回 true ,这样读者账户就不会过期,不会被锁定,也不会被撤销。
重构并重启应用后,就可以以读者身份登录应用程序了。
Spring Boot自动配置如何运作及怎样允许自己被覆盖
大部分情况下,@ConditionalOnMissingBean注解是覆盖自动配置的关键。
如Spring Boot的DataSourceAutoConfiguration中定义的JdbcTemplate Bean就是一个例子,演示了@ConditionalOnMissingBean如何工作:
@Bean
@ConditionalOnMissingBean(JdbcOperations.class)
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(this.dataSource);
}
@ConditionalOnMissingBean 注解,要求当前不存在 JdbcOperations类型( JdbcTemplate 实现了该接口)的Bean时才生效。如果当前已经有一个 JdbcOperations Bean了,条件即不满足,不会执行 jdbcTemplate() 方法。
什么情况下会存在一个 JdbcOperations Bean呢?Spring Boot的设计是加载应用级配置,随
后再考虑自动配置类。因此,如果你已经配置了一个 JdbcTemplate Bean,那么在执行自动配置
时就已经存在一个 JdbcOperations 类型的Bean了,于是忽略自动配置的 JdbcTemplate Bean。
关于Spring Security,自动配置会考虑几个配置类。在这里讨论每个配置类的细节是不切实际的,但覆盖Spring Boot自动配置的安全配置时,最重要的一个类是 SpringBootWebSecurityConfiguration 。以下是其中的一个代码片段:
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ EnableWebSecurity.class })
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
@ConditionalOnWebApplication
public class SpringBootWebSecurityConfiguration {
...
}
SpringBootWebSecurityConfiguration 上加了好几个注解。看到 @ConditionalOnClass 注解后,你就应该知道Classpath里必须要有 @EnableWebSecurity 注解。@ConditionalOnWebApplication 说 明 这 必 须 是 个 Web 应 用 程 序 。 @ConditionalOnMissingBean 注解才是我们的安全配置类代替 SpringBootWebSecurityConfiguration 的关键所在。
@ConditionalOnMissingBean 注解要求当下没有 WebSecurityConfiguration 类型的Bean。虽然表面上我们并没有这么一个Bean,但通过在 SecurityConfig 上添加 @EnableWebSecurity 注解,我们实际上间接创建了一个 WebSecurityConfiguration Bean。所以在自动配置时,这个Bean就已经存在了, @ConditionalOnMissingBean 条件不成立, SpringBootWebSecurityConfiguration 提供的配置就被跳过了。
虽然Spring Boot的自动配置和 @ConditionalOnMissingBean 让你能显式地覆盖那些可以
自动配置的Bean,但并不是每次都要做到这种程度。让我们来看看怎么通过设置几个简单的配置
属性调整自动配置组件吧。
通过属性文件外置配置
如何对配置进行微调,如修改端口号时,不放弃自动配置
Spring Boot自动配置的Bean提供了300多个微调属性。比如你想禁用属性spring.main.show-banner,将其值设置为false。有几种实现方式(这里只介绍两种):
- 创建一个名为application.properties文件,包含如下内容:
spring.main.show-banner=false
- 创建一个名为application.yml的YAM文件,内容如下:
spring:
main:
show-banner: false
实际上,Spring Boot应用程序有多种设置途径。Spring Boot能从多种属性源获得属性,包括如下几处:
(1) 命令行参数
(2) java:comp/env 里的JNDI属性
(3) JVM系统属性
(4) 操作系统环境变量
(5) 随机生成的带 random.* 前缀的属性(在设置其他属性时,可以引用它们,比如 ${random.long} )
(6) 应用程序以外的application.properties或者appliaction.yml文件
(7) 打包在应用程序内的application.properties或者appliaction.yml文件
(8) 通过 @PropertySource 标注的属性源
(9) 默认属性
这个列表按优先级排序,也就是说,任何高优先级属性源里设置的属性都会覆盖低优先级的相同属性。
application.properties和application.yml文件能放在以下四个位置:
(1) 外置,在相对于应用程序运行目录的/config子目录里。
(2) 外置,在应用程序运行的目录里。
(3) 内置,在config包内。
(4) 内置,在Classpath根目录。
同样,这个列表按照优先级排序。也就是说,/config子目录里的application.properties会覆盖
应用程序Classpath里的application.properties中的相同属性。
此外,如果你在同一优先级位置同时有application.properties和application.yml,那么application.
yml里的属性会覆盖application.properties里的属性。
自动配置微调实例
实例1:禁用模板缓存
如果你自己写了一个阅读列表(可以展示书名和增加书名),经过多个添加书名后,你会发现,除非重启应用程序,否则对Thymeleaf模板的变更是不会生效的。这是因为Thymeleaf模板默认缓存。这有助于改善应用程序的性能,因为模板只需编译一次,但在开发过程中就不能实时看到变更的效果了。
将 spring.thymeleaf.cache 设置为 false 就能禁用Thymeleaf模板缓存。
如果你希望每次运行时都禁用缓存,可以创建一个application.yml,包含以下内容:
spring:
thymeleaf:
cache: false
一定要确保这个文件不会发布到生产环境,否则生产环境里的应用程序就无法享受模板缓存带来的性能提升了。
此处使用Thymeleaf作为应用程序的视图,Spring Boot支持的其他模板也能关闭模板缓存,设置这些属性就好了:
spring.freemarker.cache (Freemarker)
spring.groovy.template.cache (Groovy模板)
spring.velocity.cache (Velocity)
默认情况下,这些属性都为 true ,也就是开启缓存。将它们设置为 false 即可禁用缓存。
实例2:配置嵌入式服务器
从命令行(或者Spring Tool Suite)运行Spring Boot应用程序时,应用程序会启动一个嵌入式
的服务器(默认是Tomcat),监听8080端口。大部分情况下这样挺好,但同时运行多个应用程序
可能会有问题。要是所有应用程序都试着让Tomcat服务器监听同一个端口,在启动第二个应用程
序时就会有冲突。
无论出于什么原因,让服务器监听不同的端口,你所要做的就是设置 server.port 属性。
例如,在Classpath根目录的application.yml文件里:
server:
port: 8000
除了服务器的端口,你还可能希望服务器提供HTTPS服务。为此,第一步就是用JDK的
keytool 工具来创建一个密钥存储(keystore):
$ keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA
该工具会询问几个与名字和组织相关的问题,大部分都无关紧要。但在被问到密码时,一定
要记住你的选择。在本例中选择letmein作为密码。
现在只需要设置几个属性就能开启嵌入式服务器的HTTPS服务了。可以把它们都配置在命令
行里,但这样太不方便了。可以把它们放在application.properties或application.yml里。在
application.yml中,它们可能是这样的:
server:
port: 8443
ssl:
key-store: file:///path/to/mykeys.jks
key-store-password: letmein
key-password: letmein
此处的 server.port 设置为8443,开发环境的HTTPS服务器大多会选这个端口。server.ssl.key-store 属性指向密钥存储文件的存放路径。这里用了一个file://开头的URL,从文件系统里加载该文件。你也可以把它打包在应用程序的JAR文件里,用 classpath: URL 来引用它。 server.ssl.key-store-password 和 server.ssl.key-password 设置为创建该文件时给定的密码。
实例3:配置日志
默认情况下,Spring Boot会用Logback(http://logback.qos.ch)来记录日志,并用 INFO 级别输出到控制台。在运行应用程序和其他例子时,你应该已经看到很多 INFO 级别的日志了。
用其他日志实现替换Logback:
一般来说,你不需要切换日志实现;Logback能很好地满足你的需要。但是,如果决定使用Log4j或者Log4j2,那么你只需要修改依赖,引入对应该日志实现的起步依赖,同时排除掉Logback。
以Maven为例,应排除掉根起步依赖传递引入的默认日志起步依赖,这样就能排除Logback了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
在Gradle里,在 configurations 下排除该起步依赖是最简单的办法:
configurations {
all*.exclude group:'org.springframework.boot',
module:'spring-boot-starter-logging'
}
排除默认日志的起步依赖后,就可以引入你想用的日志实现的起步依赖了。在Maven里可以这样添加Log4j:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
</dependency>
在Gradle里可以这样添加Log4j:
compile("org.springframework.boot:spring-boot-starter-log4j")
如果你想用Log4j2,可以把spring-boot-starter-log4j改成spring-boot-starter-log4j2。
要完全掌握日志配置,可以在Classpath的根目录(src/main/resources)里创建logback.xml文件。下面是一个logback.xml的简单例子:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="root" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
除了日志格式之外,这个Logback配置和不加logback.xml文件的默认配置差不多。但是,通过编辑logback.xml,你可以完全掌控哪些配置应该放进logback.xml。
即使如此,你对日志配置最常做的改动就是修改日志级别和指定日志输出的文件。使用了Spring Boot的配置属性后,你可以在不创建logback.xml文件的情况下修改那些配置。
要设置日志级别,你可以创建以 logging.level 开头的属性,后面是要日志名称。如果根
日志级别要设置为 WARN ,但Spring Security的日志要用 DEBUG 级别,可以在application.yml里加入以下内容:
logging:
level:
root: WARN
org:
springframework:
security: DEBUG
另外,你也可以把Spring Security的包名写成一行:
logging:
level:
root: WARN
org.springframework.security: DEBUG
现在,假设你想把日志写到位于/var/logs/目录里的BookWorm.log文件里。使用 logging.path 和 loggin.file 属性就行了:
logging:
path: /var/logs/
file: BookWorm.log
level:
root: WARN
org:
springframework:
security: DEBUG
假设应用程序有/var/logs/的写权限,日志就能被写入/var/logs/BookWorm.log。默认情况下,日志文件的大小达到10MB时会切分一次。
与之类似,这些属性也能在application.properties里设置:
logging.path=/var/logs/
logging.file=BookWorm.log
logging.level.root=WARN
logging.level.root.org.springframework.security=DEBUG
如果你还是想要完全掌控日志配置,但是又不想用logback.xml作为Logback配置的名字,可以通过 logging.config 属性指定自定义的名字:
logging:
config:
classpath:logging-config.xml
实例4:配置数据源
虽然你可以显式配置自己的 DataSource Bean,但通常并不用这么做,只需简单地通过属性配置数据库的URL和身份信息就可以了。举例来说,如果你用的是MySQL数据库,你的application.yml文件看起来可能是这样的:
spring:
datasource:
url: jdbc:mysql://localhost/readinglist
username: dbuser
password: dbpass
通常你都无需指定JDBC驱动,Spring Boot会根据数据库URL识别出需要的驱动,但如果识别出问题了,你还可以设置 spring.datasource.driver-class-name 属性:
spring:
datasource:
url: jdbc:mysql://localhost/readinglist
username: dbuser
password: dbpass
driver-class-name: com.mysql.jdbc.Driver
在自动配置 DataSource Bean的时候,Spring Boot会使用这里的连接数据。 DataSource Bean是一个连接池,如果Classpath里有Tomcat的连接池 DataSource ,那么就会使用这个连接池;否则,Spring Boot会在Classpath里查找以下连接池:
HikariCP
Commons DBCP
Commons DBCP 2
这里列出的只是自动配置支持的连接池,你还可以自己配置 DataSource Bean,使用你喜欢的各种连接池。
你也可以设置 spring.datasource.jndi-name 属性,从JNDI里查找 DataSource :
spring:
datasource:
jndi-name: java:/comp/env/jdbc/readingListDS
一旦设置了 spring.datasource.jndi-name 属性,其他数据源连接属性都会被忽略,除非没有设置别的数据源连接属性。
应用程序Bean的配置外置
先来看一个实例:
package readinglist;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
@ConfigurationProperties(prefix="amazon")//属性注入
public class ReadingListController {
private String associateId;
private ReadingListRepository readingListRepository;
@Autowired
public ReadingListController(ReadingListRepository readingListRepository) {
this.readingListRepository = readingListRepository;
}
//associateId的set方法
public void setAssociateId(String associateId) {
this.associateId = associateId;
}
@RequestMapping(method=RequestMethod.GET)
public String readersBooks(Reader reader, Model model) {
List<Book> readingList = readingListRepository.findByReader(reader);
if (readingList != null) {
model.addAttribute("books", readingList);
model.addAttribute("reader", reader);
model.addAttribute("amazonID", associateId);//将associateId放入模型
}
return "readingList";
}
@RequestMapping(method=RequestMethod.POST)
public String addToReadingList(Reader reader, Book book) {
book.setReader(reader);
readingListRepository.save(book);
return "redirect:/";
}
}
在ReadingListController中,现有一个associateId属性,及其对应的set方法,用它可以设置该属性。readersBooks()能通过amazonID这个键把associateId的值放入模型。
问题:associateId的值从何处来?
ReadingListController 上加了 @ConfigurationProperties 注解,这说明该Bean的属性应该是(通过setter方法)从配置属性值注入的。说得更具体一点, prefix 属性说明ReadingListController 应该注入带 amazon 前缀的属性。
综合起来,我们指定 ReadingListController 的属性应该从带 amazon 前缀的配置属性中进行注入。 ReadingListController 只有一个setter方法,就是设置 associateId 属性用的setter方法。因此,设置Amazon Associate ID唯一要做的就是添加 amazon.associateId 属性,把它加入支持的任一属性源位置里即可。
我们可以在application.properties里设置该属性:
amazon.associateId=habuma-20
或者在application.yml里设置:
amazon:
associateId: habuma-20
注意:Spring Boot会进行自动识别,amazon.associateId 这个属性和amazon.associate_id 以及 amazon.associate-id 都是等价的。
虽然在 ReadingListController 上加上 @ConfigurationProperties 注解跑起来没问题,但这并不是一个理想的方案。 ReadingListController 和Amazon没什么关系,但属性的前缀却是 amazon ,这看起来难道不奇怪吗?再说,后续的各种功能可能需要在 ReadingListController 里新增配置属性,而它们和Amazon无关。
与其在 ReadingListController 里加载配置属性,还不如创建一个单独的Bean,为它@ConfigurationProperties 注解,让这个Bean收集所有配置属性。
package readinglist;
import org.springframework.boot.context.properties.
ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("amazon")//注入amazon前缀属性
public class AmazonProperties {
private String associateId;
//associateId的set方法
public void setAssociateId(String associateId) {
this.associateId = associateId;
}
public String getAssociateId() {
return associateId;
}
}
package readinglist;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
public class ReadingListController {
private ReadingListRepository readingListRepository;
private AmazonProperties amazonProperties;
@Autowired
public ReadingListController(
ReadingListRepository readingListRepository,
AmazonProperties amazonProperties) {
this.readingListRepository = readingListRepository;
this.amazonProperties = amazonProperties;//注入AmazonProperties
}
@RequestMapping(method=RequestMethod.GET)
public String readersBooks(Reader reader, Model model) {
List<Book> readingList =readingListRepository.findByReader(reader);
if (readingList != null) {
model.addAttribute("books", readingList);
model.addAttribute("reader", reader);
model.addAttribute("amazonID", amazonProperties.getAssociateId());
}
return "readingList";
}
@RequestMapping(method=RequestMethod.POST)
public String addToReadingList(Reader reader, Book book) {
book.setReader(reader);
readingListRepository.save(book);
return "redirect:/";
}
}
小结:
1.定义一个配置类,注解@Component和@ConfigurationProperties(想要注入的属性前缀)
2.为配置类属性书写set方法
3.在application.yml或application.properties中写入相关属性
4.在控制类使用@Autowired,进行属性注入
使用Profile进行配置
当应用程序需要部署到不同的运行环境时,一些配置细节通常会有所不同。比如,数据库连接的细节在开发环境下和测试环境下就会不一样,在生产环境下又不一样。Spring Framework从Spring 3.1开始支持基于Profile的配置。Profile是一种条件化配置,基于运行时激活的Profile,会使用或者忽略不同的Bean或配置类。
举例来说,假设我们创建的安全配置是针对生产环境的,而自动配置的安全配置用在开发环境刚刚好。在这个例子中,我们就能为 SecurityConfig 加上 @Profile 注解:
@Profile("production")
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
这里用的 @Profile 注解要求运行时激活 production Profile,这样才能应用该配置。如果production Profile没有激活,就会忽略该配置,而此时缺少其他用于覆盖的安全配置,于是应用自动配置的安全配置。
设置 spring.profiles.active 属性就能激活Profile,任意设置配置属性的方式都能用于设置这个值。
向application.yml里添加 spring.profiles.active 属性:
spring:
profiles:
active: production
假设要对每个环境分别创建配置,可以如下方法:
- 使用特定于Profile的属性文件进行配置
如果你正在使用application.properties,可以创建额外的属性文件,遵循application-{profile}.
properties这种命名格式,这样就能提供特定于Profile的属性了。
与此同时,那些并不特定于哪个Profile或者保持默认值(以防万一有哪个特定于Profile的配
置不指定这个值)的属性,可以继续放在application.properties里 - 使用多Profile YAML文件进行配置
如果使用YAML来配置属性,则可以遵循与配置文件相同的命名规范,即创建application-{profile}.yml这样的YAML文件,并将与Profile无关的属性继续放在application.yml里。
logging:
level:
root: INFO
---
spring:
profiles: development
logging:
level:
root: DEBUG
---
spring:
profiles: production
logging:
path: /tmp/
file: BookWorm.log
level:
root: WARN
如你所见,这个application.yml文件分为三个部分,使用一组三个连字符( — )作为分隔符。
第二段和第三段分别为 spring.profiles 指定了一个值,这个值表示该部分配置应该应用在哪
个 Profile 里 。 第 二 段 中 定 义 的 属 性 应 用 于 开 发 环 境 , 因 为 spring.profiles 设 置 为
development 。与之类似,最后一段的 spring.profile 设置为 production ,在 production
Profile被激活时生效。
另一方面,第一段并未指定 spring.profiles ,因此这里的属性对全部Profile都生效,或
者对那些未设置该属性的激活Profile生效。
定制应用程序错误页面
Spring Boot自动配置的默认错误处理器会查找名为error的视图,如果找不到就用默认的白标
错误视图。因此,最简单的方法就是创建一个自定义视图,让解析出的视图名为error。
这一点归根到底取决于错误视图解析时的视图解析器。
实现了Spring的 View 接口的Bean,其 ID为 error (由Spring的 BeanNameViewResolver
所解析)。
如果配置了Thymeleaf,则有名为error.html的Thymeleaf模板。
如果配置了FreeMarker,则有名为error.ftl的FreeMarker模板。
如果配置了Velocity,则有名为error.vm的Velocity模板。
如果是用JSP视图,则有名为error.jsp的JSP模板。
Spring Boot会为错误视图提供如下错误属性。
timestamp :错误发生的时间。
status :HTTP状态码。
error :错误原因。
exception :异常的类名。
message :异常消息(如果这个错误是由异常引起的)。
errors : BindingResult 异常里的各种错误(如果这个错误是由异常引起的)。
trace :异常跟踪信息(如果这个错误是由异常引起的)。
path :错误发生时请求的URL路径。
代码实例:
<html>
<head>
<title>Oops!</title>
<link rel="stylesheet" th:href="@{/style.css}"></link>
</head>
<html>
<div class="errorPage">
<span class="oops">Oops!</span><br/>
<img th:src="@{/MissingPage.png}"></img>
<p>There seems to be a problem with the page you requested
(<span th:text="${path}"></span>).</p>
<p th:text="${'Details: ' + message}"></p>
</div>
</html>
</html>
参考文献:Spring Boot实战 ,丁雪丰 (译者)