Статьи

Использование YAML для настройки приложения Java

 YAML — это широко известный в сообществе Ruby формат , довольно широко используемый уже давно. Но мы, как разработчики Java, в основном имеем дело с файлами свойств и XML-файлами на случай, если нам понадобится некоторая конфигурация для наших приложений. Сколько раз нам нужно было выразить сложную конфигурацию, изобретая нашу собственную XML-схему или навязывая соглашение об именах свойств?

Хотя JSON становится популярным форматом для веб-приложений, использование файлов JSON для описания конфигурации немного громоздко и, на мой взгляд, не так выразительно, как YAML . Давайте посмотрим, что YAML может сделать для нас, чтобы сделать нашу жизнь проще.

Обязательно начнем с проблемы. Для того чтобы наше приложение функционировало правильно, нам нужно как-то передать ему следующие данные:

  • версия и дата выпуска
  • параметры подключения к базе данных
  • список поддерживаемых протоколов
  • список пользователей с их паролями

Этот список параметров звучит немного странно, но цель состоит в том, чтобы продемонстрировать различные типы данных в работе: строки, числа, даты, списки и карты. Модель Java состоит из двух простых классов: Connection

package com.example.yaml;

public final class Connection {
    private String url;
    private int poolSize;
  
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }

    public int getPoolSize() {
        return poolSize;
    }

    public void setPoolSize(int poolSize) {
        this.poolSize = poolSize;
    }
 
    @Override
    public String toString() {
        return String.format( "'%s' with pool of %d", getUrl(), getPoolSize() );
    }
}

и Конфигурация , оба являются типичными Java POJO, подробными из-за установщиков и получателей свойств (мы к этому привыкли, верно?).

package com.example.yaml;

import static java.lang.String.format;

import java.util.Date;
import java.util.List;
import java.util.Map;

public final class Configuration { 
    private Date released;
    private String version;
    private Connection connection;
    private List< String > protocols;
    private Map< String, String > users; 
 
    public Date getReleased() {
        return released;
    }
 
    public String getVersion() {
        return version;
    }
 
    public void setReleased(Date released) {
        this.released = released;
    }
 
    public void setVersion(String version) {
        this.version = version;
    }
 
    public Connection getConnection() {
        return connection;
    }
 
    public void setConnection(Connection connection) {
        this.connection = connection;
    }
 
    public List< String > getProtocols() {
        return protocols;
    }

    public void setProtocols(List< String > protocols) {
        this.protocols = protocols;
    }
 
    public Map< String, String > getUsers() {
        return users;
    }
 
    public void setUsers(Map< String, String > users) {
        this.users = users;
    }
 
    @Override
    public String toString() {
        return new StringBuilder()
            .append( format( "Version: %s\n", version ) )
            .append( format( "Released: %s\n", released ) )
            .append( format( "Connecting to database: %s\n", connection ) )
            .append( format( "Supported protocols: %s\n", protocols ) )
            .append( format( "Users: %s\n", users ) )
            .toString();
    }
}

Так как модель достаточно ясна, давайте попробуем выразить это так, как это обычно делает человек. Возвращаясь к нашему списку необходимых настроек, давайте попробуем записать их по очереди. 1. версия и дата выпуска

version: 1.0
released: 2012-11-30

2. параметры подключения к базе данных

connection:
    url: jdbc:mysql://localhost:3306/db
    poolSize: 5

3. список поддерживаемых протоколов

protocols:
   - http
   - https

4. список пользователей с их паролями

users:
    tom: passwd
    bob: passwd

И на этом наша конфигурация, выраженная в синтаксисе YAML, завершена! Весь файл sample.yml выглядит так:

version: 1.0
released: 2012-11-30

# Connection parameters
connection:
    url: jdbc:mysql://localhost:3306/db
    poolSize: 5

# Protocols
protocols:
   - http
   - https

# Users
users:
    tom: passwd
    bob: passwd

Чтобы заставить его работать в Java, нам просто нужно использовать потрясающую библиотеку snakeyml , соответственно файл POM Maven довольно прост:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>

    <groupid>com.example</groupid>
    <artifactid>yaml</artifactid>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
    </properties>

    <dependencies>
        <dependency>
            <groupid>org.yaml</groupid>
            <artifactid>snakeyaml</artifactid>
            <version>1.11</version>
        </dependency>
    </dependencies>
 
    <build> 
        <plugins>
            <groupid>org.apache.maven.plugins</groupid>
            <artifactid>maven-compiler-plugin</artifactid>
            <version>2.3.1</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugins>
    </build>
</project>

Обратите внимание на использование Java 1.7 , языковые расширения и дополнительные библиотеки упрощают многие обычные задачи, как мы могли видеть, рассматривая YamlConfigRunner :

package com.example.yaml;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

import org.yaml.snakeyaml.Yaml;

public class YamlConfigRunner {
    public static void main(String[] args) throws IOException {
        if( args.length != 1 ) {
            System.out.println( "Usage: <file.yml>" );
            return;
        }
  
        Yaml yaml = new Yaml();  
        try( InputStream in = Files.newInputStream( Paths.get( args[ 0 ] ) ) ) {
            Configuration config = yaml.loadAs( in, Configuration.class );
            System.out.println( config.toString() );
        }
    }
}

Фрагмент кода здесь загружает конфигурацию из файла ( args [0] ), пытается проанализировать ее и заполнить класс Configuration значимыми данными, используя соглашения JavaBeans , преобразуя, где это возможно, в объявленные типы. Выполнение этого класса с sample.yml в качестве аргумента приводит к следующему выводу:

Version: 1.0
Released: Thu Nov 29 19:00:00 EST 2012
Connecting to database: 'jdbc:mysql://localhost:3306/db' with pool of 5
Supported protocols: [http, https]
Users: {tom=passwd, bob=passwd}

Полностью идентичен значениям, которые мы настроили!