学习Spring Boot基本配置

170 阅读7分钟

Spring Boot基本配置

在软件开发中,根据你的团队所处的阶段,会发生很多变化。例如,你可能需要从一个测试数据库转变为一个可用于生产的数据库。不管是什么,你的代码运行环境会发生变化,你的配置也会发生变化。

在本指南中,我们将探讨Spring Boot为我们提供的一些策略,以应对这一挑战。我们将看看如何改变环境,提取环境变量,以及更多。

在本文结束时,你将更有能力应对Spring Boot的配置挑战。

先决条件

要完成这篇文章,你需要具备以下条件。

  • 有关Spring Boot的基本知识(即Bean、服务、依赖注入)。
  • Java基础知识
  • 打包和运行jar文件(可选,但建议)。

什么是配置文件?

在谈论环境配置时,必须了解什么是profile。Spring中的profile是指你想运行应用程序的环境名称。

你的配置文件的名字可以是你想要的任何名字,可以代表你想要的任何环境。两个普遍存在的是开发和生产环境。

使用配置文件,我们可以改变某些配置变量以适应环境。默认情况下,你的Spring Boot应用程序将有一个默认的配置文件。当然,这也可以被覆盖以满足你的需求。

设置配置文件

要设置配置文件,你可以在打包或运行jar 文件时添加一个命令行参数。这个命令行参数被命名为Dspring.profiles.active ,其值应该是你想要使用的配置文件。

例如,如果你想用prod的配置文件来运行你的jar ,你可以这样运行一个命令。

java -jar -Dspring.profiles.active=prod my-project-0.0.1-SNAPSHOT.jar

如果你想从IntelliJ运行你的应用程序,你可以在你的运行配置中设置活动的配置文件。你首先要点击运行,然后编辑配置,你应该看到一个输入来设置你的虚拟机选项。

run-config

在这里,你可以添加上面的命令参数。如果你使用的是IntelliJ Ultimate,应该有一个单独的输入字段来放置配置文件。

注意:你可以在运行你的应用程序时设置多个配置文件。为了简单起见,我们在本指南中每次只使用一个配置文件。

使用配置文件

配置文件的具体内容

现在我们知道了如何设置配置文件,那么我们该如何利用这个优势呢?

Spring Boot允许我们根据配置文件来覆盖配置文件中的值。

要做到这一点,你要在与application.properties 文件相同的位置创建一个新的属性文件。这些文件需要遵循这个特定的命名规则,以区分配置文件。

application-{profile}.properties

如果配置文件处于活动状态,这些文件中的属性将覆盖你的application.properties 。在一个开发配置文件中,application-dev.properties 将覆盖application.properties 中的普通属性。

请注意,我们可以把自定义属性放在这些文件中,这样我们就可以在我们的代码中使用它们。它并不局限于你所习惯看到的内置属性。我们将在后面介绍如何做到这一点。

隐藏敏感属性

假设你正在创建一个application-prod.properties 文件。当然,这将包含你的生产就绪的应用程序的属性。这些细节应该是私有的,因为它们涉及到实际的数据库或秘密密钥。

那么,我们怎样才能采取适当的措施来保护这些数据呢?

实现这一目标的一个好方法是将系统变量注入到你的配置文件中。当你安装Java的时候,你必须经历设置一个叫做JAVA_HOME的系统变量的过程。这个变量存储了你的JDK的文件夹位置。

像这样的系统变量可以被注入你的配置文件中。

test.property=${JAVA_HOME}

利用这一点,你可以在运行你的服务器的设备上将敏感数据设置为系统变量。

例如,如果你使用Heroku托管你的应用程序,他们会给你一个选项来设置称为config vars的系统变量。这有一个额外的好处,就是以后很容易改变。

外部配置

比方说,你把一个可以生产的应用程序打包成一个jar。突然间,你意识到你需要改变配置文件中的一个属性。

然后你意识到这个文件也被打包到了jar中,这就需要你解压这个jar。你怎么能在不重新启动服务器的情况下对配置文件进行修改呢?

Spring Boot使用外部化配置为这个问题提供了一个简单的解决方案。你所需要做的就是在与你的jar文件相同的目录下创建一个新的配置文件。你可以用这个新的配置文件来覆盖jar里面的任何配置。

注意,在这种情况下,你的命令行需要在运行jar时指向你的jar所在的文件夹。我相信这是因为它看的是你所处的当前目录的外部化配置。

使用Value注解

你可能想知道,我们怎样才能使用配置文件中的值?Spring Boot为我们提供了一些策略来做到这一点。

使用@Value 注解,我们可以将配置文件中的值注入Bean中的字段。要做到这一点,我们将使用以下模式。

package me.john.amiscaray.demo.controllers;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    // Inject the test.property variable above into the javaHome field.
    @Value("${test.property}")
    private String javaHome;

    @GetMapping("/")
    public String test(){

        return javaHome;

    }

}

注意我们是如何用大括号把属性名称包起来的,前面还有一个美元符号。如果我们不这样做,它将把字符串字面内容注入字段,而不是test.property

如果你向根URL发送一个GET请求,你应该看到你的JDK的位置。

使用ConfigurationProperties注解

另一种提取配置变量的方法是通过@ConfigurationProperties 注解。这个注解允许你将每一个带有特定前缀的配置变量注入Bean中。

假设你有一个像这样的application.properties 文件。

app.property.name=My cool app
app.property.creator=John Amiscaray
app.property.version=5.9.0

通过这个注解,你可以创建一个包含所有这些值的Bean,就像这样。

package me.john.amiscaray.demo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

// Make this a bean using the @Configuration annotation.
@Configuration
// Try to extract all the config variables with the prefix "app.property" into the fields here.
@ConfigurationProperties("app.property")
public class AppProperties {

    private String name;
    private String creator;
    private String version;

    // Getters and setters below.

}

在这里,我们首先使用@Configuration 注解将我们的类声明为一个bean。然后我们使用带有app.property值的@ConfigurationProperties 注解。这指定了我们要获得任何带有该前缀的属性。

然后我们声明与我们的配置变量名称相匹配的字段。

这些变量就是带有给定前缀的变量。这样一来,Spring将在name、creator和version字段中分别注入My cool appJohn Amiscaray5.9.0这些值。

在你的代码中使用配置文件

到目前为止,我们已经研究了如何根据你的配置文件设置配置变量并在你的代码中提取它们。

现在我们将看看你如何根据你所处的配置文件来改变你代码中的行为。我很少发现这方面的好用例,但当时机成熟时,它可能会很有帮助。

使用配置文件注解

你可以使用@Profile 注解来激活某些豆子;这取决于配置文件。为了说明这一点,请看下面的例子。

注意:我不建议使用下面的架构。我使用这个架构只是作为一个简单的例子。

假设你有下面这个接口。

package me.john.amiscaray.demo.config;

public interface AppEnvironment {

    String getJWTSecret();
    String getClientUrl();

}

你可以像这样为开发环境实现它。

@Profile("dev")
@Configuration
public class DevEnvironment implements AppEnvironment{

    @Override
    public String getJWTSecret() {
        return "secret";
    }

    @Override
    public String getClientUrl() {
        return "http://localhost:8100";
    }

}

还有一个用于开发环境的接口。

package me.john.amiscaray.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Profile("prod")
@Configuration
public class ProdEnvironment implements AppEnvironment{

    @Override
    public String getJWTSecret() {
        return "this is a more secure secret you'll never guess";
    }

    @Override
    public String getClientUrl() {
        return "Some URL";
    }

}

从那里,你可以将一个类型为AppEnvironment 的对象注入你的Spring Bean中。如果是开发环境,它将使用第一个类;如果是生产环境,它将使用第二个。

同样的方法也可以使用注有@Bean 的方法来实现。这是有可能的,因为@Profile 也适用于带有@Bean 注解的方法。

正如我之前所说,我反对使用这样的架构。相反,我会使用@ConfigurationProperties 注解和特定配置文件。

这样一来,我们就可以使用外部化的配置来获得未来的灵活性。在这种新的方法中,我们甚至可以使用一些特定的配置文件将其缩短为一个类。

使用环境Bean

Spring Boot还为你提供了一个类型为Environment 的预制Bean。这个Bean可以用来查找活动的配置文件,并获得配置变量。

我很少使用这个Bean,因为有更好的方法来获取配置变量。我也几乎没有发现自己需要在代码中知道我所处的环境。

因此,我们不会对这个类进行太多详细的介绍,特别是你可以很容易地阅读文档。

然而,我发现这个豆子有一个小的用例。

对于我正在创建的一个特定的服务,我让一个列表只能在这个类中访问。尽管为了测试,我希望能够改变和查看该列表的状态。

作为一个解决方案,我像这样使用环境bean。

package me.john.amiscaray.demo.service;

import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
public class ExampleService {

    private final Environment environment;
    private final List<String> privateList = new ArrayList<>();

    public ExampleService(Environment environment){
        this.environment = environment;
    }

    /**
    * Returns the list for testing purposes. If not in the test environment, throw an exception.
    * @return the private list
    * @throws IllegalAccessException if called in a non-test environment
    */
    public List<String> getPrivateList() throws IllegalAccessException {
        if(!Arrays.asList(environment.getActiveProfiles()).contains("test")){
            throw new IllegalAccessException("This is only available in test environments");
        }
        return privateList;
    }

}

总的来说,虽然不是那么有用,但是知道这个bean的存在对于像这样的小用例还是很方便的。

总结

在本指南中,我们探讨了如何配置Spring Boot应用程序以处理环境的变化。我们研究了Spring Boot如何表示环境(作为配置文件),以及如何根据环境改变你的应用程序的行为。

有了这些,你就可以开始使用Spring Boot创建更出色、更可重用的应用程序。