В процессе практики мы часто сталкиваемся с ситуациями, когда для разных окружений требуются разные конфигурационные файлы, будет проблематично повторно модифицировать конфигурационный файл или переупаковывать его каждый раз при смене окружения, Spring Boot предоставляет Profile configuration для решения этой проблемы.
Роль профиля
Профиль не имеет подходящего перевода на китайский язык. Его основная функция — позволить Spring Boot предоставлять различную поддержку конфигурации в зависимости от разных сред.
Мы часто сталкиваемся с такими сценариями: есть среды разработки, тестирования, производства и другие, и разные среды имеют разные конфигурации. Если каждой среде необходимо изменить файл конфигурации во время развертывания, это будет очень хлопотно, но с помощью Профиля проблема может быть легко решена.
Основное использование профиля
Например, в приведенной выше среде мы можем создать 4 файла в Spring Boot:
- applcation.properties: общедоступная конфигурация
- application-dev.properties: конфигурация среды разработки
- application-test.properties: конфигурация тестовой среды
- application-prod.properties: конфигурация производственной среды
Настройте общедоступную конфигурацию в applcation.properties, а затем активируйте конфигурацию для указанной среды со следующей конфигурацией:
spring.profiles.active = prod
Среди них «prod» сравнивается с application-prod.properties в имени файла. Spring Boot получит файл конфигурации applcation.properties во время обработки, а затем объединит его через значение «prod» указанного профиля, чтобы получить имя и путь к файлу application-prod.properties.
Например, для служб в среде разработки используется порт 8080, но в производственной среде необходимо использовать порт 18080. Затем настройте в application-prod.properties следующее:
server.port=8080
А в application-prod.properties настраивается как:
server.port=18080
При использовании разных сред его можно указать, настроив значение свойства spring.profiles.active в applcation.properties (как в приведенном выше примере), или его можно указать с помощью параметров запуска команды:
java -jar springboot.jar --spring.profiles.active=prod
Таким образом, упакованная программа может использовать файлы конфигурации разных сред только через параметры командной строки.
На основе типа файла yml
Если файл конфигурации основан на типе файла yml, вы также можете поместить всю конфигурацию в один и тот же файл конфигурации:
spring:
profiles:
active: prod
server:
port: 18080
---
spring:
profiles: dev
server:
port: 8080
---
spring:
profiles: test
server:
port: 8081
Вышеупомянутая конфигурация разделена "---", и первая позиция является профилем по умолчанию.Например, когда вышеуказанная конфигурация запускается, порт 18080 используется по умолчанию. Если вы хотите использовать указанный порт, вы также можете использовать приведенную выше команду для указания при запуске.
Анализ исходного кода
Давайте кратко рассмотрим основной поток обработки для профиля в Spring Boot (без чрезмерного уточнения конкретных операций). Когда Spring Boot выполняет свой метод run во время запуска, выполняется следующий фрагмент кода:
public ConfigurableApplicationContext run(String... args) {
// ...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// ...
}
// ...
}
Соответствующий код метода prepareEnvironment выглядит следующим образом:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// ...
listeners.environmentPrepared(environment);
// ...
}
В ходе бизнес-процесса метода prepareEnvironment для публикации событий будет вызываться метод environmentPrepared класса SpringApplicationRunListeners. Этот метод будет проходить через класс реализации SpringApplicationRunListener, зарегистрированный в spring.factories, а затем вызывать его метод environmentPrepared.
Класс реализации SpringApplicationRunListener в spring.factories зарегистрирован как:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
Код вызова в методе SpringApplicationRunListeners выглядит следующим образом:
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
Среди них listeners — это набор зарегистрированных классов, по умолчанию есть только EventPublishingRunListener. Его метод environmentPrepared реализован как:
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
По сути, он публикует событие прослушивателя. Это событие будет прослушиваться ConfigFileApplicationListener, также зарегистрированным в spring.factories:
# Application Listeners
org.springframework.context.ApplicationListener=\
// ...
org.springframework.boot.context.config.ConfigFileApplicationListener
// ...
Основная обработка файлов конфигурации выполняется в ConfigFileApplicationListener. В этом классе мы можем увидеть много знакомых констант:
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private static final String DEFAULT_PROPERTIES = "defaultProperties";
// Note the order is from least to most specific (last one wins)
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";
// ...
}
Например, имя файла конфигурации, который Spring Boot ищет по умолчанию, путь к классам, сканируемый по умолчанию, и т. д.
Мало того, этот класс также реализует интерфейс слушателя SmartApplicationListener и интерфейс EnvironmentPostProcessor, то есть имеет функции слушателя и обработки среды.
Его метод onApplicationEvent оценивает полученное событие. Если это событие ApplicationEnvironmentPreparedEvent, он вызывает для обработки метод onApplicationEnvironmentPreparedEvent. Код выглядит следующим образом:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
onApplicationEnvironmentPreparedEvent получит соответствующий EnvironmentPostProcessor и вызовет его метод postProcessEnvironment для обработки. EnvironmentPostProcessor, полученный методом loadPostProcessors, является текущим классом, настроенным в spring.factories.
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
После серии вызовов наконец вызываются следующие методы класса:
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
Класс Loader — это внутренний класс ConfigFileApplicationListener, который предоставляет такие функции, как обработка приоритета файла конфигурации, профиля, а также загрузка и синтаксический анализ.
Например, в методе load класса Loader есть следующий код:
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
Код просматривает список PropertySourceLoader и анализирует соответствующий файл конфигурации, а PropertySourceLoader в этом списке также настроен в spring.factories:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
Глядя на исходный код этих двух загрузчиков PropertySourceLoader, вы обнаружите, что формат файла конфигурации и метод синтаксического анализа поддерживаются SpringBoot по умолчанию.
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
private static final String XML_FILE_EXTENSION = ".xml";
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
Map<String, ?> properties = loadProperties(resource);
// ...
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, ?> loadProperties(Resource resource) throws IOException {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
return (Map) PropertiesLoaderUtils.loadProperties(resource);
}
return new OriginTrackedPropertiesLoader(resource).load();
}
}
Например, PropertiesPropertySourceLoader поддерживает файлы конфигурации в двух форматах, xml и properties, и предоставляет два класса, PropertiesLoaderUtils и OriginTrackedPropertiesLoader, для соответствующей обработки.
Тот же YamlPropertySourceLoader поддерживает файлы конфигурации в форматах yml и yaml, а для парсинга использует класс OriginTrackedYamlLoader.
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
// ...
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
// ...
}
}
Конечно, реализация того, как соединить файл конфигурации по умолчанию и профиль, упомянутый выше, также реализована в классе ConfigFileApplicationListener.Соответствующий код выглядит следующим образом:
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// ...
if (profile != null) {
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// ...
}
Класс ConfigFileApplicationListener также реализует другие функции, если вам интересно, вы можете отладить, чтобы прочитать его.
Оригинальная ссылка: "Профиль SpringBoot использует подробное объяснение и анализ исходного кода конфигурации》
Весеннее техническое видео
Академия CSDN:«Семейный блок видеоуроков Spring Boot»