从本质上来说,Spring Boot就是Spring,它做了那些没有它你自己也会去做的SpringBean配置。谢天谢地,幸好有Spring,你不用再写这些样板配置了,可以专注于应用程序的逻辑,这些才是应用程序独一无二的东西。
spring的介绍
启动引导spring
package readinglist;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReadingListApplication {
public static void main(String[] args) {
SpringApplication.run(ReadingListApplication.class, args);
}
}
@SpringBootApplication:开启组件扫描和自动配置
SpringApplication.run 负责启动引导应用程序
@SpringBootApplication 将三个有用的注解组合在了一起。
- Spring的 @Configuration :标明该类使用Spring基于Java的配置。虽然本书不会写太多
配置,但我们会更倾向于使用基于Java而不是XML的配置。 - Spring的 @ComponentScan :启用组件扫描,这样你写的Web控制器类和其他组件才能被
自动发现并注册为Spring应用程序上下文里的Bean。本章稍后会写一个简单的Spring MVC
控制器,使用 @Controller 进行注解,这样组件扫描才能找到它。 - Spring Boot 的 @EnableAutoConfiguration : 这 个 不 起 眼 的 小 注 解 也 可 以 称 为
@Abracadabra
① ,就是这一行配置开启了Spring Boot自动配置的魔力,让你不用再写成
篇的配置了。
spring的自动配置
在maven的依赖里面我们会看到一些starter之类的依赖,这个是启动依赖,比如hibernate,在依赖之后spring就会为我们做一个自动配置的事情,那么我们如果想要自定义的配置的话,就需要用自定义的配置来覆盖这个自定义的。
这里有个不错的例子:当你在应用程序里添加安全特性时,自动配置做得还不够好。安全配置并不是放之四海而皆准的,围绕应用程序安全有很多决策要做,Spring Boot不能替你做决定。
虽然Spring Boot为安全提供了一些基本的自动配置,但是你还是需要自己覆盖一些配置以满足特定的安全要求。想知道如何用显式的配置来覆盖自动配置,我们先从为阅读列表应用程序添加SpringSecurity入手。在了解自动配置提供了什么之后,我们再来覆盖基础的安全配置,以满足特定的
场景需求。
首先我们配置一个
@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')")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true");
}
3.1 覆盖 Spring Boot自动配置
@Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return readerRepository.findOne(username);
}
});
}
}
那么自动配置那边是怎么检测到这个类然后并且覆盖自动配置类呢?
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ EnableWebSecurity.class })
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
@ConditionalOnWebApplication
public class SpringBootWebSecurityConfiguration {
...
}
如你所见, SpringBootWebSecurityConfiguration 上加了好几个注解。看到 @ConditionalOnClass 注解后,你就应该知道Classpath里必须要有 @EnableWebSecurity 注解。
@ConditionalOnWebApplication 说 明 这 必 须 是 个 Web 应 用 程 序 。 @ConditionalOn-MissingBean 注解才是我们的安全配置类代替 SpringBootWebSecurityConfiguration 的关键所在。
@ConditionalOnMissingBean 注解要求当下没有WebSecurityConfiguration 类型的Bean。虽然表面上我们并没有这么一个Bean,但通过在 SecurityConfig 上添加 @EnableWeb-Security 注解,我们实际上间接创建了一个 WebSecurityConfiguration Bean。所以在自动
配置时,这个Bean就已经存在了, @ConditionalOnMissingBean 条件不成立, SpringBoot-WebSecurityConfiguration 提供的配置就被跳过了。
虽然Spring Boot的自动配置和 @ConditionalOnMissingBean 让你能显式地覆盖那些可以自动配置的Bean,但并不是每次都要做到这种程度。让我们来看看怎么通过设置几个简单的配置属性调整自动配置组件吧。
通过属性文件外置配置
在处理应用安全时,你当然会希望完全掌控所有配置。不过,为了微调一些细节,比如改改端口号和日志级别,便放弃自动配置,这是一件让人羞愧的事。为了设置数据库URL,是配置一个属性简单,还是完整地声明一个数据源的Bean简单?答案不言自明,不是吗?事实上,Spring Boot自动配置的Bean提供了300多个用于微调的属性。当你调整设置时,只要在环境变量、Java系统属性、JNDI(Java Naming and Directory Interface)、命令行参数或者属性文件里进行指定就好了
- 比如springboot程序开启时候会有一个banner就是一个图形化的代码出现,如果你想要禁用的话,就在application.properties文件里面加上配置:spring.main.show-banner=false,当然如果你喜欢的话,也可以创建名为application.yml的YAML文件:
spring:
main:
show-banner: false
- 比如你想要修改tomcat的端口号:
server:
port: 8000
- 要设置日志级别,你可以创建以 logging.level 开头的属性,后面是要日志名称。如果根日志级别要设置为 WARN ,但Spring Security的日志要用 DEBUG 级别,可以在application.yml里加入
logging:
path: /var/logs/
file: BookWorm.log
level:
root: WARN
org:
springframework:
security: DEBUG
另外,你也可以把Spring Security的包名写成一行:
logging:
level:
root: WARN
org.springframework.security: DEBUG
- 配置数据源
spring:
datasource:
url: jdbc:mysql://localhost/readinglist
username: dbuser
password: dbpass
driver-class-name: com.mysql.jdbc.Driver
以下内容:
实际上,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根目录。
应用程序 Bean 的配置外置
@RequestMapping("/")
@ConfigurationProperties(prefix="amazon")
public class ReadingListController {
private String associateId;
private ReadingListRepository readingListRepository;
@Autowired
public ReadingListController(
ReadingListRepository readingListRepository) {
this.readingListRepository = readingListRepository;
}
public void setAssociateId(String associateId) {
this.associateId = associateId;
}
如你所见, ReadingListController 现在有了一个 associateId 属性,还有对应的 setAssociateId() 方法,用它可以设置该属性。 readersBooks() 现在能通过 amazonID 这个键把associateId 的值放入模型。
棒极了!现在就剩一个问题了——从哪里能取到 associateId 的值。
请注意, ReadingListController 上加了 @ConfigurationProperties 注解,这说明该Bean的属性应该是(通过setter方法)从配置属性值注入的。说得更具体一点, prefix 属性说明ReadingListController 应该注入带 amazon 前缀的属性。
综合起来,我们指定 ReadingListController 的属性应该从带 amazon 前缀的配置属性中进行注入。 ReadingListController 只有一个setter方法,就是设置 associateId 属性用的setter方法。因此,设置Amazon Associate ID唯一要做的就是添加 amazon.associateId 属性,把它加入支持的任一属性源位置里即可。
在application.yml里设置:
amazon:
associateId: habuma-20
在一个类里收集属性
虽然在 ReadingListController 上加上 @ConfigurationProperties 注解跑起来没问题,但这并不是一个理想的方案。 ReadingListController 和Amazon没什么关系,但属性的前缀却是 amazon ,这看起来难道不奇怪吗?再说,后续的各种功能可能需要在 ReadingListController 里新增配置属性,而它们和Amazon无关。
与其在 ReadingListController 里加载配置属性,还不如创建一个单独的Bean,为它加上@ConfigurationProperties 注解,让这个Bean收集所有配置属性。代码清单3-5里的 Amazon-Properties 就是一个例子,它用于加载Amazon相关的配置属性。
@Component
@ConfigurationProperties("amazon")
public class AmazonProperties {
private String associateId;
public void setAssociateId(String associateId) {
this.associateId = associateId;
}
public String getAssociateId() {
return associateId;
}
}
有了加载 amazon.associateId 配置属性的 AmazonProperties 后,我们可以调整
ReadingListController (如代码清单3-6所示),让它从注入的 AmazonProperties 中获取
Amazon Associate ID。
使用 Profile 进行配置
当应用程序需要部署到不同的运行环境时,一些配置细节通常会有所不同。比如,数据库连接的细节在开发环境下和测试环境下就会不一样,在生产环境下又不一样。Spring Framework从Spring 3.1开始支持基于Profile的配置。Profile是一种条件化配置,基于运行时激活的Profile,会使用或者忽略不同的Bean或配置类。
@Profile("production")
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
这里用的 @Profile 注解要求运行时激活 production Profile,这样才能应用该配置。如果production Profile没有激活,就会忽略该配置,而此时缺少其他用于覆盖的安全配置,于是应用自动配置的安全配置。
向application.yml里添加 spring.profiles.active 属性:
spring:
profiles:
active: production
使用多Profile YAML文件进行配置
如果使用YAML来配置属性,则可以遵循与配置文件相同的命名规范,即创建application-{profile}.yml这样的YAML文件,并将与Profile无关的属性继续放在application.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