配置
我们可以很快地重写Spring Boot的默认配置。默认情况下,应用的配置可以使用Java属性文件来进行定义,这个文件名为application.properties并且位于应用的classpath根目录下。不过,一种更好的方式是使用 YAML配置,它提供了结构化以及嵌套的配置。在应用的运行时类路径之中包含snakeyaml之后,你的工程就可以在application.yml文件中直接定义配置了。为了详述这一点,考虑程序清单1.13的示例YAML配置,这里列出了应用的嵌入式HTTP服务器(默认是Tomcat,也可选择Jetty)的各种设置项。
程序清单1.13
# Server settings (ServerProperties)
server:
port: 8080
address:127.0.0.1
sessionTimeout:30
contextPath: /
# Tomcatspecifics
tomcat:
accessLogEnabled: false
protocolHeader:x-forwarded-proto
remoteIpHeader:x-forwarded-for
basedir:
backgroundProcessorDelay: 30 # secs
允许重写Boot的自动化配置,这一点能够使你的应用从原型转化为真正的产品,Boot使用相同的application.yml文件进行配置,这样就会非常容易。自动化配置的指令被设计的尽可能简短,所以当使用actuator构建微服务时,会安装一个配置属性的端点,也就是/configprops,当确定哪些指令需要重写时可以进行参考。如果我们的微服务要使用持久化数据源,如MySQL,那么只需将MySQL的Java驱动添加到运行时classpath中,然后在application.yml中添加必要的配置指令即可,如程序清单1.14所示。
程序清单1.14
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/proddb
username: root
password
在一些场景下你可能需要更为灵活的配置,Boot允许你通过Java的系统属性(System properties)重写很多它的默认配置。例如,如果你的应用需要在部署到产品化环境中使用不同的数据库用户,那么username配置指令可以通过标准的Java系统属性传入到应用之中,而这需要切换到命令行中执行-Dspring.datasource.username=user。关于这一点更为现实的场景是云部署环境,如Cloud Foundry或Heroku,这些平台需要应用启动特定的HTTP端口,这一点通过操作系统的环境变量可以实现。Boot能够从系统属性继承得到配置,这样你的应用就可以在命令行中使用-Dserver.port=$PORT来得到HTTP端口。在开发微服务时,这是一种相当有用的特性,因为它可以让微服务应用运行在各种环境配置之中。
外部化配置
微服务必须要支持的很重要的一点就是外部化配置。这种配置可以包含任何的内容,从占位符信息到数据库配置等等,在初始规划和构建应用原型时,这是必须要考虑的架构内容。在Spring IO平台中,已经存在各种导入配置的策略,但是应用能够以多种方式使用配置所造成的后果往往是产生冗长的编码性耦合。
Boot一个很棒的特性在于它能管理外部化的配置并将其转换为对象结构,这个对象可以在整个应用上下文中使用。创建一个简单老式的Java/Groovy对象(Plain Old Java/Groovy Object),并使用@ConfigurationProperties注解,那么这个对象就能使用Boot配置结构中预先定义的name名下的配置项。更具体一点来讲,考虑一下程序清单1.15中的POGO,它能够得到application.key下的配置指令。
程序清单1.15
@ConfigurationProperties(name = "application")
class ApplicationProperties {
String name
String version
}
当ApplicationProperties对象在Spring上下文中创建完成之后,Boot将会识别出它是一个配置对象,并且会按照运行时classpath之中application.properties或application.yml文件中的配置指令填充它的属性。因此,如果我们在微服务的application.yml文件中添加application内容区的话,如程序清单1.16所示,那么我们就可以在应用的其他部分以编程的方式访问这些配置指令。
程序清单1.16
application:
name:sb-ms-custdepl
version:0.1-CUSTOMER
这些配置指令可以有各种用途,要访问这些指令的唯一要求就是代表它们的POJO/POGO必须是Spring应用上下文的成员。Boot能够将一个controller作为Spring Java配置对象,这样就能很容易地管理配置bean与应用上下文的集成,如程序清单1.17所示。
程序清单1.17
@RestController
@Configuration
@RequestMapping("/appinfo")
@EnableAutoConfiguration
class AppInfoController {
@Autowired
ApplicationProperties applicationProperties
@RequestMapping(method=[RequestMethod.GET])
def get() {
[
name:applicationProperties.name,
version:applicationProperties.version
]
}
@Bean
ApplicationProperties applicationProperties() {
newApplicationProperties()
}
public staticvoid main(String[] args) {
SpringApplication.run UserController, args
}
}
程序清单1.17中的样例代码可能有些牵强,不过,即便是在更为复杂的场景下,如何使用Boot来访问应用特定配置的原则是相同的。配置类也支持嵌套式的对象图,这样来自于配置中的深层数据就能更便利地进行访问,也有了更好的语义。例如,如果我们想要得到的配置指令是application.根下的那些metrics key,那么可以在ApplicationProperties POGO中添加一个嵌套对象来表示这些值,如程序清单1.18所示。
程序清单1.18
@ConfigurationProperties(name = "application")
class ApplicationProperties {
String name
String version
final Metricsmetrics = new Metrics()
static classMetrics {
StringdbExecutionTimeKey
}
}
现在,我们的application.yml文件可以如程序清单1.19所示,它在application.代码块中包含了metrics配置。
程序清单1.19
application:
name:sb-ms-custdepl
version:0.1-CUSTOMER
metrics:
dbExecutionTimeKey: user.get.db.time
当我们需要访问application.metrics.dbExecutionTimeKey的值时,能够以编程的方式通过ApplicationProperties对象来进行访问。
为了在整个应用之中使用application.properties或application.yml文件中的这些配置指令,我们并不是必须要将其转换为对象图。Boot也为Spring应用上下文提供了PropertySourcesPlaceholderConfiguration,这样的话,来自于application.properties或application.yml文件的指令或者来自于Java系统的重写属性都可以作为Spring属性占位符来使用。Spring的这种机制能够让你以一种特定的语法来为属性定义占位符值,如果Spring发现了占位符配置的话,就会用这个配置来进行填充。作为示例,我们可以在controller中使用@Value注解来直接访问application.metrics.dbExecutionTimeKey,如程序清单1.20所示。
程序清单1.20
@RestController
@RequestMapping("/user")
@EnableAutoConfiguration
class UserController {
@Autowired
UserRepositoryrepository
@Autowired
GaugeServicegaugeService
@Value('${application.metrics.dbExecutionTimeKey}')
StringdbExecutionKey
@RequestMapping(method=[RequestMethod.GET])
def get(Long id){
def start = newDate().time
def result = id? repository.findOne(id) : repository.findAll()
gaugeService.submitdbExecutionKey, new Date().time - start
result
}
public staticvoid main(String[] args) {
SpringApplication.run UserController, args
}
}
关于应用指标的报告,后面会有更为详细的介绍,但现在重要的一点在于,理解@Value注解如何与Spring属性占位符一起使用,使Boot能够自动注入值,从而满足这个微服务的特定配置需求。