Um guia para os metadados da configuração de inicialização Spring

Um guia para os metadados da configuração de inicialização Spring

1. Visão geral

Ao escrever um aplicativo Spring Boot, é útil paramap configuration properties onto Java beans. Qual é a melhor maneira de documentar essas propriedades?

Neste tutorial, vamos explorar osSpring Boot Configuration Processorethe associated JSON metadata files que documentam o significado de cada propriedade, restrições e assim por diante.

2. Metadados de configuração

A maioria dos aplicativos em que trabalhamos como desenvolvedores deve ser configurável até certo ponto. No entanto, normalmente, não entendemos realmente o que um parâmetro de configuração faz, se ele tem um valor padrão, se está obsoleto e, às vezes, nem sabemos que a propriedade existe.

Para nos ajudar, o Spring Boot gera metadados de configuração em um arquivo JSON, que fornece informações úteis sobre como usar as propriedades. Então,the configuration metadata is a descriptive file which contains the necessary information for interaction with the configuration properties.

A coisa realmente legal sobre este arquivo é queIDEs can read it, too, nos dando autocompletar das propriedades do Spring, bem como outras dicas de configuração.

3. Dependências

Para gerar esses metadados de configuração, usaremos o processador de configuração despring-boot-configuration-processor dependency.

Então, vamos continuar e adicionar a dependência comooptional:


    org.springframework.boot
    spring-boot-configuration-processor
    2.1.7.RELEASE
    true

Essa dependência nos fornecerá um processador de anotação Java chamado quando construímos nosso projeto. Falaremos em detalhes sobre isso mais tarde.

É uma prática recomendada adicionar uma dependência comooptional no Maven para evitar que@ConfigurationProperties seja aplicado a outros módulos que nosso projeto usa.

4. Exemplo de propriedades de configuração

Para ver o processador em ação, vamos imaginar que temos algumas propriedades que precisamos incluir em nosso aplicativo Spring Boot por meio de um bean Java:

@Configuration
@ConfigurationProperties(prefix = "database")
public class DatabaseProperties {

    public static class Server {

        private String ip;
        private int port;

        // standard getters and setters
    }

    private String username;
    private String password;
    private Server server;

    // standard getters and setters
}

Para fazer isso, usaríamos a anotação@ConfigurationProperties. The configuration processor scans for classes and methods with this annotation para acessar os parâmetros de configuração e gerar metadados de configuração.

Vamos adicionar algumas dessas propriedades em um arquivo de propriedades. Nesse caso, vamos chamá-lo dedatabaseproperties-test.properties:

#Simple Properties
database.username=example
database.password=password

E, só para ter certeza, também adicionaremos um teste para ter certeza de que estamos todos alinhados:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AnnotationProcessorApplication.class)
@TestPropertySource("classpath:databaseproperties-test.properties")
public class DatabasePropertiesIntegrationTest {

    @Autowired
    private DatabaseProperties databaseProperties;

    @Test
    public void whenSimplePropertyQueriedThenReturnsPropertyValue()
      throws Exception {
        Assert.assertEquals("Incorrectly bound Username property",
          "example", databaseProperties.getUsername());
        Assert.assertEquals("Incorrectly bound Password property",
          "password", databaseProperties.getPassword());
    }

}

Também adicionamos as propriedades aninhadasdatabase.server.idedatabase.server.port por meio da classe internaServer. We should add the inner classServeras well as a fieldserverwith its own getter and setter.

Em nosso teste, vamos fazer uma verificação rápida para garantir que também possamos definir e ler as propriedades aninhadas com êxito:

@Test
public void whenNestedPropertyQueriedThenReturnsPropertyValue()
  throws Exception {
    Assert.assertEquals("Incorrectly bound Server IP nested property",
      "127.0.0.1", databaseProperties.getServer().getIp());
    Assert.assertEquals("Incorrectly bound Server Port nested property",
      3306, databaseProperties.getServer().getPort());
}

Ok, agora estamos prontos para usar o processador.

5. Gerando Metadados de Configuração

Mencionamos anteriormente que o processador de configuração gera um arquivo - ele usa o processamento de anotações.

Então, depois de compilar nosso projeto, veremos umfile calledspring-configuration-metadata.jsoninsidetarget/classes/META-INF:

{
  "groups": [
    {
      "name": "database",
      "type": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server",
      "type": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceMethod": "getServer()"
    }
  ],
  "properties": [
    {
      "name": "database.password",
      "type": "java.lang.String",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server.ip",
      "type": "java.lang.String",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties$Server"
    },
    {
      "name": "database.server.port",
      "type": "java.lang.Integer",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "defaultValue": 0
    },
    {
      "name": "database.username",
      "type": "java.lang.String",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties"
    }
  ],
  "hints": []
}

A seguir, vamos ver como alterar as anotações em nossos beans Java afetam os metadados.

5.1. Informações adicionais sobre metadados de configuração

Primeiro, vamos adicionar comentários JavaDoc emServer.

Em segundo lugar, vamos dar um valor padrão para o campodatabase.server.port e finalmente adicionar as anotações@Mine@Max:

public static class Server {

    /**
     * The IP of the database server
     */
    private String ip;

    /**
     * The Port of the database server.
     * The Default value is 443.
     * The allowed values are in the range 400-4000.
     */
    @Min(400)
    @Max(800)
    private int port = 443;

    // standard getters and setters
}

Se verificarmos o arquivospring-configuration-metadata.json agora, veremos esta informação extra refletida:

{
  "groups": [
    {
      "name": "database",
      "type": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server",
      "type": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceMethod": "getServer()"
    }
  ],
  "properties": [
    {
      "name": "database.password",
      "type": "java.lang.String",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server.ip",
      "type": "java.lang.String",
      "description": "The IP of the database server",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties$Server"
    },
    {
      "name": "database.server.port",
      "type": "java.lang.Integer",
      "description": "The Port of the database server. The Default value is 443.
        The allowed values are in the range 400-4000",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "defaultValue": 443
    },
    {
      "name": "database.username",
      "type": "java.lang.String",
      "sourceType": "com.example.autoconfiguration.annotationprocessor.DatabaseProperties"
    }
  ],
  "hints": []
}

Podemos verificar as diferenças com os camposdatabase.server.ipedatabase.server.port . De fato, a informação extra é bastante útil. Como resultado, é muito mais fácil para desenvolvedores e IDEs entender o que cada propriedade faz.

Também devemos garantir que acionamos a compilação para obter o arquivo atualizado. No Eclipse, se marcarmos a opçãoBuild Automatically, cada ação de salvamento acionará um build. No IntelliJ, devemos disparar a compilação manualmente.

5.2. Compreendendo o formato de metadados

Vamos dar uma olhada no arquivo de metadados JSON e discutir seus componentes.

Groups são itens de nível superior usados ​​para agrupar outras propriedades, sem especificar um valor em si. Em nosso exemplo, temos o grupodatabase, que também é o prefixo das propriedades de configuração. Também temos um gruposerver, que criamos por meio de uma classe interna e as propriedadesipeport dos grupos.

Properties são itens de configuração para os quais podemos especificar um valor. Essas propriedades são definidas nos arquivos.properties ou.yml e podem conter informações extras, como valores padrão e validações, como vimos no exemplo acima.

Hints são informações adicionais para ajudar o usuário a definir o valor da propriedade. Por exemplo, se tivermos um conjunto de valores permitidos para uma propriedade, podemos fornecer uma descrição do que cada um deles faz. O IDE fornecerá ajuda de competição automática para essas dicas.

Cada componente nos metadados de configuração tem seu próprioattributes para explicar em detalhes as propriedades de configuração.

6. Conclusão

Neste artigo, analisamos o processador de inicialização Spring e sua capacidade de criar metadados de configuração. O uso desses metadados facilita muito a interação com nossos parâmetros de configuração.

Nós demos um exemplo de metadados de configuração gerados e explicamos em detalhes seu formato e componentes.

Também vimos como o suporte ao preenchimento automático em nosso IDE pode ser útil.

Como sempre, todos os trechos de código mencionados neste artigo podem ser encontrados emour GitHub repository.