- PVSM.RU - https://www.pvsm.ru -

Книга «Java в облаке. Spring Boot, Spring Cloud, Cloud Foundry»

image [1] Всем привет! В основном данная книга предназначена для разработчиков Java- и JVM-машин, которые ищут способы создания более качественного ПО в короткие сроки с помощью Spring Boot, Spring Cloud и Cloud Foundry. Она для тех, кто уже слышал шум, поднявшийся вокруг микросервисов. Возможно, вы уже поняли, на какую стратосферную высоту взлетела среда Spring Boot, и удивляетесь тому, что сегодня предприятия используют платформу Cloud Foundry. Если так и есть, то эта книга для вас.

Отрывок. 3. Стиль конфигурации двенадцатифакторных приложений

В этой главе будет рассмотрен порядок реализации конфигурации приложения.

Определим ряд словарных терминов. Когда речь заходит о конфигурации в Spring, чаще всего имеется в виду ввод в среду Spring различных реализаций контекста приложения — ApplicationContext [2], что помогает контейнеру понять, как связать bean-компоненты. Такую конфигурацию можно представить в виде файла в формате XML, который должен быть подан в ClassPathXmlApplicationContext [3], или классов Java, аннотированных способом, позволяющим быть предоставленными объекту AnnotationConfigApplicationContext [4]. И конечно же, при изучении последнего варианта мы станем ссылаться на конфигурацию Java.

Но в этой главе мы собираемся рассмотреть конфигурацию в том виде, в котором она определена в манифесте 12-факторного приложения [5]. В данном случае она касается буквальных значений, способных изменяться от одной среды окружения к другой: речь идет о паролях, портах и именах хостов или же о флагах свойств. Конфигурация игнорирует встроенные в код магические константы. В манифест включен отличный критерий правильности настройки конфигурации: может ли кодовая база приложения быть открытым источником в любой момент без раскрытия и компрометации важных учетных данных? Эта разновидность конфигурации относится исключительно к тем значениям, которые изменяются от одной среды окружения к другой, и не относится, например, к подключению bean-компонентов Spring или конфигурации маршрутов Ruby.

Поддержка во фреймворке Spring

В Spring стиль конфигурации, соответствующий 12 факторам, поддерживается с появления класса PropertyPlaceholderConfigurer [6]. Как только определяется его экземпляр, он заменяет литералы в XML-конфигурации значениями, извлеченными из файла с расширением .properties. В среде Spring класс PropertyPlaceholderConfigurer [7] предлагается с 2003 года. В Spring 2.5 появилась поддержка пространства имен XML, а вместе с тем и поддержка в данном пространстве подстановки свойств. Это позволяет проводить подстановку в XML-конфигурации литеральных значений определений bean-компонентов значениями, назначенными ключам во внешнем файле свойств (в данном случае в файле simple.properties, который может фигурировать в пути к классам или быть внешним по отношению к приложению).

Конфигурация в стиле 12 факторов нацелена на устранение ненадежности име­ющихся магических строк, то есть значений наподобие адресов баз данных и учетных записей для подключения к ним, портов и т.д., жестко заданных в скомпилированном приложении. Если конфигурация вынесена за пределы приложения, то ее можно заменить, не прибегая для этого к новой сборке кода.

Класс PropertyPlaceholderConfigurer

Посмотрим образец использования класса PropertyPlaceholderConfigurer, XML-определений bean-компонентов Spring и вынесенного за пределы приложения файла с расширением .properties. Нам нужно просто вывести значение, имеющееся в данном файле свойств. Это поможет сделать код, показанный в примере 3.1.

Пример 3.1. Файл свойств: some.properties

configuration.projectName=Spring Framework

Это принадлежащий Spring класс ClassPathXmlApplicationContext, таким образом, мы используем пространство имен XML из контекста Spring и указываем на наш файл some.properties. Затем в определениях bean-компонентов задействуем литералы в форме ${configuration.projectName}, и Spring в ходе выполнения заменит их значениями из нашего файла свойств (пример 3.2).

Пример 3.2. XML-файл Spring-конфигурации

<context:property-placeholder location="classpath:some.properties"/> (1)

<bean class="classic.Application">
       <property name="configurationProjectName"
       value="${configuration.projectName}"/>
</bean>

(1) classpath: местоположение, ссылающееся на файл в текущем откомпилированном блоке кода (.jar, .war и т. д.). Spring поддерживает множество альтернативных вариантов, включая file: и url:, позволяющих файлу существовать обособленно от блока кода.

И наконец, рассмотрим, как выглядит класс Java, благодаря которому возможно свести все это воедино (пример 3.3).

Пример 3.3. Класс Java, который должен быть сконфигурирован со значением свойства
package classic;
 

import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
			 
public class Application {
			 
  public static void main(String[] args) {
    new ClassPathXmlApplicationContext("classic.xml");
  }
			 
  public void setConfigurationProjectName(String pn) {
    LogFactory.getLog(getClass()).info("the configuration project name is " + pn);
  }
}

В первом примере используется XML-формат конфигурации bean-компонентов Spring. В Spring 3.0 и 3.1 ситуация для разработчиков, применяющих конфигурацию Java, значительно улучшилась. В этих выпусках были введены аннотация Value [8] и абстракция Environment.

Абстракция Environment и Value [8]

Абстракция Environment [9] представляет в ходе выполнения кода его косвенное отношение к той среде окружения, в которой он запущен, и позволяет приложению ставить вопрос («Какой разделитель строк line.separator на данной платформе?») о свойствах среды. Абстракция действует в качестве отображения из ключей и значений. Сконфигурировав в Environment источник свойств PropertySource, можно настроить то место, откуда эти значения будут считываться. По умолчанию Spring загружает системные ключи и значения среды, такие как line.separator. Системе Spring можно предписать загрузку ключей конфигурации из файла в том же порядке, который мог бы использоваться в ранних выпусках решения Spring по подстановке свойств с помощью аннотации @PropertySource.

Аннотация Value [8] предоставляет способ внедрения значений среды окружения в конструкторы, сеттеры, поля и т. д. Эти значения могут быть вычислены с помощью языка выражений Spring Expression Language или синтаксиса подстановки свойств при условии регистрации PropertySourcesPlaceholderConfigurer [10], как сделано в примере 3.4.

Пример 3.4. Регистрация PropertySourcesPlaceholderConfigurer
package env;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;

import javax.annotation.PostConstruct;

(1)
@Configuration
@PropertySource("some.properties")
public class Application {

  private final Log log = LogFactory.getLog(getClass());

  public static void main(String[] args) throws Throwable {
    new AnnotationConfigApplicationContext(Application.class);
  }

(2)
@Bean
static PropertySourcesPlaceholderConfigurer pspc() {
 return new PropertySourcesPlaceholderConfigurer();
}

(3)
@Value("${configuration.projectName}")
private String fieldValue;

(4)
@Autowired
Application(@Value("${configuration.projectName}") String pn) {
  log.info("Application constructor: " + pn);
}

(5)
@Value("${configuration.projectName}")
void setProjectName(String projectName) {
  log.info("setProjectName: " + projectName);
}

(6)
@Autowired
void setEnvironment(Environment env) {
  log.info("setEnvironment: " + env.getProperty("configuration.projectName"));
}

(7)
@Bean
InitializingBean both(Environment env,
  @Value("${configuration.projectName}") String projectName) {
   return () -> {
     log.info("@Bean with both dependencies (projectName): " + projectName);
     log.info("@Bean with both dependencies (env): "
      + env.getProperty("configuration.projectName"));
   };
 }

 @PostConstruct
 void afterPropertiesSet() throws Throwable {
   log.info("fieldValue: " + this.fieldValue);
 }
}

(1) Аннотация @PropertySource — сокращение наподобие property-placeholder, настраивающее PropertySource из файла с расширением .properties.

(2) PropertySourcesPlaceholderConfigurer нужно зарегистрировать в качестве статического bean-компонента, поскольку он является реализацией BeanFactoryPostProcessor и должен вызываться на ранней стадии жизненного цикла инициализации в Spring bean-компонентов. При использовании в Spring XML-конфигурации bean-компонентов этот нюанс не просматривается.

(3) Можно отдекорировать поля аннотацией Value [8] (но не делайте этого, иначе код не пройдет тестирование!)…

(4) …или аннотацией Value [8] можно отдекорировать параметры конструктора…

(5) …или воспользоваться методами установки…

(6) …или внедрить объект Spring Environment и выполнить разрешение ключа вручную.

(7) Параметры с аннотацией Value [8] можно использовать также в поставщике аргументов методов Bean [11] в Java-конфигурации Spring.

В этом примере значения загружаются из файла simple.properties, а затем в нем имеется значение configuration.projectName, предоставляемое различными способами.

Профили

Кроме всего прочего, абстракция Environment вводит профили [12]. Это позволяет приписывать метки (профили) в целях группировки bean-компонентов. Профили следует использовать для описания bean-компонентов и bean-графов, изменяющихся от среды к среде. Одновременно могут активироваться сразу несколько профилей. Bean-компоненты, не имеющие назначенных им профилей, активируются всегда. Bean-компоненты, имеющие профиль default, активируются только в том случае, если у них нет других активных профилей. Атрибут profile можно указать в определении bean-компонента в XML либо в классах тегов, классах конфигурации, отдельно взятых bean-компонентах или в методах Bean [11]-поставщика с помощью Profile [13].

Профили позволяют описывать наборы bean-компонентов, которые должны быть созданы в одной среде несколько иначе, чем в другой. В локальном разработочном dev-профиле можно, к примеру, воспользоваться встроенным источником данных H2 javax.sql.DataSource, а затем, когда активен prod-профиль, переключиться на источник данных javax.sql.DataSource для PostgreSQL, получаемый с помощью JNDI-поиска или путем чтения свойств из переменной среды в Cloud Foundry [14]. В обоих случаях ваш код будет работоспособен: вы получаете javax.sql.DataSource, но решение о том, какой конкретный экземпляр задействовать, принимается с помощью активации одного профиля или нескольких (пример 3.5).

Пример 3.5. Демонстрация того, что классы @Configuration могут загружать различные файлы конфигурации и предоставлять различные bean-компоненты на основе
активного профиля

package profiles;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

@Configuration
public class Application {

  private Log log = LogFactory.getLog(getClass());

  @Bean
  static PropertySourcesPlaceholderConfigurer pspc() {
    return new PropertySourcesPlaceholderConfigurer();
  }

(1)
 @Configuration
 @Profile("prod")
 @PropertySource("some-prod.properties")
  public static class ProdConfiguration {

   @Bean
    InitializingBean init() {
     return () -> LogFactory.getLog(getClass()).info("prod InitializingBean");
    }
  }

@Configuration
@Profile({ "default", "dev" })

(2)
@PropertySource("some.properties")
public static class DefaultConfiguration {

  @Bean
   InitializingBean init() {
     return () -> LogFactory.getLog(getClass()).info("default InitializingBean");
   }
 }

(3)
 @Bean
  InitializingBean which(Environment e,
                                @Value("${configuration.projectName}") String
 projectName) {
    return () -> {
      log.info("activeProfiles: '"
               + StringUtils.arrayToCommaDelimitedString(e.getActiveProfiles()) +
                  "'");
      log.info("configuration.projectName: " + projectName);
    };
 }

 public static void main(String[] args) {
   AnnotationConfigApplicationContext ac = new
   AnnotationConfigApplicationContext();
   ac.getEnvironment().setActiveProfiles("dev"); (4)
   ac.register(Application.class);
   ac.refresh();
  }
}

(1) Этот класс конфигурации и все имеющиеся в нем определения Bean [11] будут вычислены только в случае активности prod-профиля.

(2) Данный класс конфигурации и все имеющиеся в нем определения Bean [11] будут вычислены, только если активен dev-профиль или не активен ни один профиль, включая dev.

(3) Этот компонент InitializingBean просто записывает текущий активный профиль и вводит значение, которое в конечном итоге было внесено в файл свойств.

(4) Активировать профиль (или профили) программным способом довольно просто.

Spring откликается еще на несколько других методов активации профилей, использующих токен spring_profiles_active или spring.profiles.active. Профиль можно установить с помощью переменной среды (например, SPRING_PROFILES_ACTIVE), JVM-свойства (‑Dspring.profiles.active=… ), параметра инициализации сервлет-приложения или программным способом.

Конфигурация Bootiful

Spring Boot [15] существенно улучшает ситуацию. Среда изначально автоматически загрузит свойства из иерархии заранее известных мест. Аргументы командной строки переопределяют значения свойств, полученных из JNDI, которые переопределяют свойства, полученные из System.getProperties(), и т. д.

— Аргументы командной строки.
— Атрибуты JNDI из java:comp/env.
— Свойства System.getProperties().
— Переменные среды операционной системы.
— Внешние файлы свойств в файловой системе: (config/)?application.(yml.properties).
— Внутренние файлы свойств в архиве (config/)?application.(yml.properties).
— Аннотация @PropertySource в классах конфигурации.
— Исходные свойства из SpringApplication.getDefaultProperties().

В случае активности профиля [16] будут автоматически считаны данные из файлов конфигурации, основанных на имени профиля, например из такого файла, как src/main/resources/application-foo.properties, где foo — текущий профиль.

Если библиотека SnakeYAML [17] упомянута в путях к классам (classpath), то будут также автоматически загружены YAML-файлы, следуя в основном тому же соглашению.

На странице спецификации YAML [18] указано, что «YAML является удобным для человеческого восприятия стандартом сериализации данных для всех языков программирования». YAML — иерархическое представление значений. В обычных файлах с расширением .properties иерархия обозначается с помощью точки («.»), а в YAML-файлах используется символ новой строки и дополнительный уровень отступа. Было бы неплохо воспользоваться этими файлами, чтобы избежать необходимости указывать общие корни при наличии сильно разветвленных деревьев конфигурации.

Содержимое файла с расширением .yml показано в примере 3.6.

Пример 3.6. Файл свойств application.yml. Данные изложены в иерархическом порядке

configuration:
   projectName : Spring Boot
management:
   security:
      enabled: false

Кроме того, среда Spring Boot существенно упрощает получение правильного результата в общих случаях. Она превращает аргументы -D в переменные процесса и среды java, доступные в качестве свойств. Она даже проводит их нормализацию, при которой переменная среды $CONFIGURATION_PROJECTNAME (КОНФИГУРАЦИЯ_ИМЯПРОЕКТА) или аргумент -D в форме ‑Dconfiguration.projectName (конфигурация.имя_проекта) становятся доступными с помощью ключа configuration.projectName (конфигурация.имя_проекта) точно так же, как ранее был доступен токен spring_profiles_active.

Значения конфигурации являются строками и при их достаточном количестве могут стать неудобочитаемыми при попытке убедиться, что такие ключи не стали сами по себе магическими строками в коде. В Spring Boot вводится тип компонента @ConfigurationProperties. При аннотировании POJO — Plain Old Java Object (обычный старый объект Java) — с помощью @ConfigurationProperties и указании префикса среда Spring предпримет попытку отобразить все свойства, начинающиеся с этого префикса, на POJO-свойства. В показанном ниже примере значение для configuration.projectName будет отображено на экземпляр POJO, который весь код затем может внедрить и разыменовать для типобезопасного чтения значений. Как следствие, у вас будет только отображение из (String) ключа в одном месте (пример 3.7).

Пример 3.7. Автоматическое разрешение свойств из src/main/resources/application.yml
package boot;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

(1)
@EnableConfigurationProperties
@SpringBootApplication
public class Application {

  private final Log log = LogFactory.getLog(getClass());

  public static void main(String[] args) {
    SpringApplication.run(Application.class);
  }

  @Autowired
   public Application(ConfigurationProjectProperties cp) {
     log.info("configurationProjectProperties.projectName = "
     + cp.getProjectName());
   }
 }

(2)
@Component
@ConfigurationProperties("configuration")
class ConfigurationProjectProperties {

 private String projectName; (3)
 public String getProjectName() {
   return projectName;
 }

 public void setProjectName(String projectName) {
   this.projectName = projectName;
 }
}

(1) Аннотация @EnableConfigurationProperties предписывает среде Spring отображать свойства на POJO-объекты, аннотированные с помощью @ConfigurationProperties.

(2) Аннотация @ConfigurationProperties показывает среде Spring, что этот bean-компонент должен использоваться как корневой для всех свойств, начинающихся с configuration., с последующими токенами, отображаемыми на свойства объекта.

(3) Поле projectName будет в конечном итоге иметь значение, присвоенное ключу свойства configuration.projectName.

Spring Boot активно применяет механизм @ConfigurationProperties, чтобы дать пользователям возможность переопределять элементарные составляющие системы. Можно заметить, что ключи свойств позволяют вносить изменения, например, путем добавления зависимости org.springframework.boot:spring-boot-starter-actuator в веб-приложение на основе Spring Boot с последующим посещением страницы 127.0.0.1 [19]:8080/configprops.

Конечные точки актуатора более подробно будут рассмотрены в главе 13. Они заперты и требуют по умолчанию имени пользователя и пароля. Меры безопасности можно отключить (но только чтобы взглянуть на эти точки), указав management.security.enabled=false в файле application.properties (или в application.yml).

Вы получите список поддерживаемых свойств конфигурации на основе типов, представленных в путях к классам (classpath) во время выполнения. По мере наращивания количества типов Spring Boot будут показываться дополнительные свойства. В этой конечной точке также станут отображаться свойства, экспортированные вашими POJO-объектами, имеющими аннотацию @ConfigurationProperties.

Централизованная регистрируемая конфигурация с использованием сервера конфигурации Spring Cloud

Пока все хорошо, однако нужно, чтобы дела шли еще успешнее. Мы по-прежнему не ответили на вопросы, касающиеся общих случаев применения:

  • после изменений, внесенных в конфигурацию приложения, потребуется перезапуск;
  • отсутствует прослеживаемость: как определить изменения, введенные в эксплуатацию, и при необходимости выполнить их откат?
  • конфигурация децентрализована; не сразу видно, куда следует вносить изменения, чтобы изменить тот или иной аспект;
  • отсутствует установочная поддержка для кодирования и декодирования в целях безопасности.

Сервер конфигурации Spring Cloud

Проблему централизации конфигурации можно решить, сохранив конфигурацию в одном каталоге и указав всем приложениям на него. Можно также установить управление версиями этого каталога, используя Git или Subversion. Тогда будет получена поддержка, необходимая для проверки и регистрирования. Но последние два требования по-прежнему не будут выполнены, поэтому нужно нечто более изощренное. Обратимся к серверу конфигурации Spring Cloud [20]. Платформа Spring Cloud предлагает сервер конфигурации и клиента для этого сервера.

Сервер Spring Cloud Config представляет собой REST API, к которому будут подключаться наши клиенты, чтобы забирать свою конфигурацию. Сервер также управляет хранилищем конфигураций с управлением версиями. Он посредник между нашими клиентами и хранилищем конфигурации и таким образом находится в выгодной позиции, позволяющей внедрять средства обеспечения безопасности подключений со стороны клиентов к сервису и подключений со стороны сервиса к хранилищу конфигураций с управлением версиями. Клиент Spring Cloud Config предоставляет клиентским приложениям новую область видимости, refresh, дающую возможность конфигурировать компоненты Spring заново, не прибегая к перезапуску приложения.

Технологии, подобные серверу Spring Cloud Config, играют важную роль, но влекут дополнительные рабочие издержки. В идеале эта обязанность должна быть переложена на платформу и автоматизирована. При использовании Cloud Foundry в каталоге сервисов можно найти сервис Config Server, действия которого основаны на применении сервера Spring Cloud Config.

Рассмотрим простой пример. Сначала настроим сервер Spring Cloud Config. К одному такому сервису могут иметь доступ сразу несколько приложений Spring Boot. Вам нужно где-то и как-то заставить его функционировать. Затем останется только оповестить все наши сервисы о том, где найти сервис конфигурации. Он работает как некий посредник для ключей конфигурации и значений, которые он считывает из Git-хранилища по сети или с диска. Добавьте к сборке вашего приложения Spring Boot строку org.springframework.cloud: spring-cloud-config-server, чтобы ввести сервер Spring Cloud Config (пример 3.8).

Пример 3.8. Чтобы встроить в сборку сервер конфигурации, воспользуйтесь аннотацией @EnableConfigServer

package demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

(1)
@SpringBootApplication
@EnableConfigServer
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

(1) Использование аннотации @EnableConfigServer приводит к установке сервера Spring Cloud Config.

В примере 3.9 показана конфигурация для сервиса конфигурации.

Пример 3.9. Конфигурация сервера конфигурации src/main/resources/application.yml

server.port=8888
spring.cloud.config.server.git.uri=
github.com/cloud-native-java/config-server-configuration-repository [21] (1)

(1) Указание на работающее Git-хранилище, имеющее локальный характер либо доступное по сети (например, на GitHub (https://github.com/)) и используемое сервером Spring Cloud Config.

Здесь сервису конфигурации Spring Cloud предписывается выполнить поиск файлов конфигурации для отдельно взятых клиентов в Git-хранилище на GitHub. Мы указали на это хранилище, но подошла бы ссылка на любой действующий Git URI. Разумеется, он даже не обязан относится к Git-системе, можно воспользоваться Subversion или даже неуправляемыми каталогами (хотя мы настоятельно не рекомендуем делать это). В данном случае URI хранилища жестко задан, но нет ничего, что помешает получить его из аргумента -D, аргумента — или из переменной среды окружения.

» Более подробно с книгой можно ознакомиться на сайте издательства [22]
» Оглавление [23]
» Отрывок [24]

Для Хаброжителей скидка 20% по купону — Java

Автор: ph_piter

Источник [25]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/294606

Ссылки в тексте:

[1] Image: https://habr.com/company/piter/blog/425109/

[2] ApplicationContext: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html

[3] ClassPathXmlApplicationContext: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/ClassPathXmlApplicationContext.html

[4] AnnotationConfigApplicationContext: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/AnnotationConfigApplicationContext.html

[5] 12-факторного приложения: https://12factor.net/config

[6] PropertyPlaceholderConfigurer: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html

[7] PropertyPlaceholderConfigurer: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html

[8] Value: https://habr.com/users/value/

[9] Environment: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/env/Environment.html

[10] PropertySourcesPlaceholderConfigurer: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.html

[11] Bean: https://habr.com/users/bean/

[12] профили: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html

[13] Profile: https://habr.com/users/profile/

[14] Cloud Foundry: https://cloudfoundry.org/

[15] Spring Boot: http://spring.io/projects/spring-boot

[16] активности профиля: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

[17] SnakeYAML: https://bitbucket.org/asomov/snakeyaml

[18] YAML: http://yaml.org/

[19] 127.0.0.1: http://127.0.0.1

[20] Spring Cloud: http://cloud.spring.io/spring-cloud-config/

[21] github.com/cloud-native-java/config-server-configuration-repository: https://github.com/cloud-native-java/config-server-configuration-repository

[22] сайте издательства: https://www.piter.com/product/java-v-oblake-spring-boot-spring-cloud-cloud-foundry

[23] Оглавление: https://storage.piter.com/upload/contents/978544610713/978544610713_X.pdf

[24] Отрывок: https://storage.piter.com/upload/contents/978544610713/978544610713_p.pdf

[25] Источник: https://habr.com/post/425109/?utm_campaign=425109