三、自定义配置

在向应用程序加入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的自动配置。


默认的登录页面.png

覆盖自动配置的显示安全配置:

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里的属性
  1. 自动配置微调

在Spring Boot里有300多个属性可以用来微调应用程序里的Bean。
例如,通过application.yml来更改嵌入式服务器端口:

server:
  port: 8000
  1. 应用程序的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;
    }
}
  1. 使用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
  1. 使用特定于Profile的属性文件

如果你正在使用application.properties,可以创建额外的属性文件,遵循application-{profile}.properties这种命名格式,这样就能提供特定于Profile的属性了。

  1. 使用多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路径
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容