5.1微调自动配置

5.1微调自动配置

在我们深入研究配置属性之前,有必要确定在Spring中有两种不同(但相关)的配置

  • Bean wiring —— 它声明应用程序组件将在Spring应用程序上下文中作为bean创建,以及它们应该如何相互注入。
  • Property injection —— 在Spring应用程序上下文中设置bean的值。

SpringXML和基于Java的配置中,这两种类型的配置通常在同一个地方显式地声明。在Java配置中,@Bean注解的方法可能实例化一个bean,然后设置其属性的值。例如,考虑下面的@Bean方法,它为嵌入式H2数据库声明了一个数据源:

@Bean
public DataSource dataSource() {
    return new EmbeddedDataSourceBuilder()
        .setType(H2)
        .addScript("taco_schema.sql")
        .addScripts("user_data.sql", "ingredient_data.sql")
        .build();
}

这里的addScript()addScripts()方法设置了一些带有SQL脚本名称的字符串属性,这些SQL脚本应该在数据源准备好后应用到数据库中。如果不使用Spring Boot,那么这就是配置DataSource bean的方式,而自动配置使此方法完全没有必要。

如果H2依赖项在运行时类路径中可用,那么Spring Boot将在Spring应用程序上下文中自动创建适当的数据源beanbean应用于schema.sqldata.sql脚本的读取。

但是,如果希望将SQL脚本命名为其他名称呢?或者,如果需要指定两个以上的SQL脚本怎么办?这就是配置属性的用武之地。但是在开始使用配置属性之前,需要了解这些属性的来源。

5.1.1理解Spring环境抽象

Spring环境抽象是任何可配置属性的一站式商店。它抽象了属性的起源,以便需要这些属性的bean可以从Spring本身使用它们。Spring环境来自几个属性源,包括:

  • JVM系统属性

  • 操作系统环境变量

  • 命令行参数

  • 应用程序属性配置文件

然后,它将这些属性聚合到单一的源中,从这个源中可以注入Spring bean。图5.1演示了来自属性源的属性是如何通过Spring环境抽象流到Spring bean中的。

![5.1](E:\Document\spring-in-action-v5-translate\第一部分Spring基础\第五章 使用配置属性\5.1.jpg)

5.1 Spring环境从属性源获取属性,并使它们能够在应用程序上下文中的bean获取

通过Spring Boot自动配置的bean都可以通过从Spring环境中提取的属性进行配置。作为一个简单的例子,假设希望应用程序的底层servlet容器侦听某些端口上的请求,而不是默认端口8080。为此,通过在src/main/resources/application.properties文件中的server.port属性来指定一个不同的接口,如下所示:

server.port=9090

就我个人而言,我更喜欢在设置配置属性时使用YAML。因此,我可能设置在/src/main/resources/application.yml文件中的server.port的值,而不是使用application.properties文件,如下所示:

server:
  port: 9090

如果希望在外部配置该属性,还可以在启动应用程序时使用命令行参数指定端口:

$ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090

如果想让应用程序总是在一个特定的端口上启动,可以把它设置为一个操作系统环境变量:

$ export SERVER_PORT=9090

注意,在将属性设置为环境变量时,命名风格略有不同,以适应操作系统对环境变量名称的限制。Spring能够将其分类并将SERVER_PORT转译为server.port

正如我所说的,有几种设置配置属性的方法。当我们讲到第14章的时候,你会看到在一个集中的配置服务器中设置配置属性的另一种方法。实际上,可以使用几百个配置属性来调整Spring bean的行为。你已经看到了一些:本章中的server.port和前一章的security.user.namesecurity.user.password

在本章中不可能测试所有可用的配置属性。尽管如此,让我们来看看一些可能经常遇到的最有用的配置属性。我们将从几个属性开始,这些属性允许你调整自动配置的数据源。

5.1.2配置数据源

此时,Taco Cloud应用程序仍未完成,但是在准备部署应用程序之前,还有几个章节要处理一些问题。因此,作为数据源使用的嵌入式H2数据库非常适合目前为止需要的一切。但是,一旦将应用程序投入生产,可能需要考虑一个更持久的数据库解决方案。

虽然可以显式地配置DataSource bean,但这通常是不必要的。相反,通过配置属性为数据库配置URL和凭据更简单。例如,如果打算开始使用MySQL数据库,可以将以下配置属性添加到application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost/tacocloud
    username: tacodb
    password: tacopassword

虽然需要将适当的JDBC驱动程序添加到构建中,但通常不需要指定JDBC驱动程序类;Spring Boot可以从数据库URL的结构中找到它。但如果有问题,可以试着设置spring.datasource.schemaspring.datasource.data属性:

spring:
  datasource:
    schema:
      - order-schema.sql
      - ingredient-schema.sql
      - tao-schema.sql
      - user-schema.sql
    data:
      - ingredients.sql

可能显式数据源配置不是你的风格。相反,你可能更喜欢在JNDI中配置数据源,并让Spring从那里查找它。在这种情况下,通过配置spring.datasource.jndi-name来设置数据源:

spring:
  datasource:
    jndi-name: java:/comp/env/jdbc/tacoCloudDS

如果设置了spring.datasource.jndi-name属性,那么其他数据源的连接属性(如果设置了)会被忽略。

5.1.3配置嵌入式服务器

已经看到如何通过设置server.port来设置servlet容器。还没有让你看到的是,如果把server.port设置为0会发生什么:

server:
  port: 0

尽管正在显式地设置server.port0,但是服务器不会在端口0上启动。相反,它将从随机选择的可用端口启动。这在运行自动化集成测试以确保任何并发运行的测试不会在硬编码端口号上发生冲突时非常有用。在第13章中将看到,当不关心应用程序启动于哪个端口时,它也很有用,因为它是一个将从服务注册表中查找的微服务。

但是底层服务器不仅仅是一个端口。需要对底层容器做的最常见的事情之一是将其设置为处理HTTPS请求。要做到这一点,你必须做的第一件事是通过使用JDKkeytool命令行工具创建一个密钥存储:

$ keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA

你将会被问到几个关于你的名字和公司的问题,这些问题大部分都是无关紧要的。但当被要求输入密码时,记住你的密码。对于本例,我选择letmein作为密码。

接下来,需要设置一些属性,用于在嵌入式服务器中启用HTTPS。可以在命令行中指定它们,但是那样会非常不方便。相反,可能会在application.propertiesapplication.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-passwordserver.ssl.key-password属性都被设置为创建密钥存储时指定的密码值。

有了这些属性,应用程序应该侦听端口8443上的HTTPS请求。根据使用的浏览器,可能会遇到服务器无法验证其身份的警告。在开发期间从本地主机提供服务时,这没有什么可担心的。

5.1.4配置日志

大多数应用程序都提供某种形式的日志记录。即使应用程序没有直接记录任何内容,应用程序使用的库也肯定会记录它们的活动。

默认情况下,Spring Boot通过 Logback 配置日志,默认为INFO级别,然后写入控制台。在运行应用程序和其他示例时,可能已经在应用程序日志中看到了大量的INFO级别的日志条目。

要完全控制日志配置,可以在类路径的根目录(在src/main/resources中)创建log .xml文件。下面是一个简单的log .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配置或多或少与没有log .xml文件时得到的默认配置相同。但是通过编辑logback.xml,可以完全控制应用程序的日志文件。

注意logback.xml中包含的具体内容超出了本书的范围。有关更多信息,请参阅Logback的文档。

对日志配置最常见的更改是更改日志级别,可能还会指定应该写入日志的文件。使用Spring Boot配置属性,可以在不创建log .xml文件的情况下进行这些更改。

要设置日志记录级别,需要创建以logging.level为前缀的属性,后面接上要为其设置日志级别的日志记录器的名称。例如,假设想将root日志级别设置为WARN,但是将Spring安全日志设置为DEBUG级别。可以像下面这样设置:

logging:
  level:
    root: WARN
    org:
      springframework:
        security: DEBUG

另外,可以将Spring Security包的名称折叠成一行,以便于阅读:

logging:
  level:
    root: WARN
    org:
      springframework.security: DEBUG

现在,假设希望将日志条目写入位于/var/logs/文件夹下的TacoCloud.log文件。loggin.pathlogging.file属性可以帮助实现这一点:

logging:
  path: /var/logs/
  file: TacoCloud.log
  level:
    root: WARN
    org:
      springframework:
        security: DEBUG

假设应用程序对/var/logs/文件夹有写权限,那么日志将被写到/var/logs/TacoCloud.log文件中。默认情况下,日志文件在大小达到10 MB时就会进行循环写入。

5.1.5使用特殊的属性值

在设置属性时,不限于将它们的值声明为硬编码的字符串和数值。相反,可以从其他配置属性派生它们的值。

例如,假设(不管出于什么原因)想要设置一个名为greeting.welcome的属性,用于返回另一个名为spring.application.name的属性的值。为此,在设置greeting.welcome时可以使用 ${} 占位符标记:

greeting:
  welcome: ${spring.application.name}

你甚至可以把这个占位符嵌入到其他文本中:

greeting:
  welcome: You are using ${spring.application.name}

正如你所看到的,使用配置属性配置Spring自己的组件可以很容易地将值注入这些组件的属性并调整自动配置。配置属性并不专属于Spring创建的bean。只需稍加努力,就可以利用你自己的bean中的配置属性。接下来让我们来看看怎么做。

下一页