Spring Boot: возможно ли использовать внешние файлы application.properties в произвольных каталогах с толстой флягой?

Можно ли иметь несколько файлов application.properties? (РЕДАКТИРОВАТЬ: обратите внимание, что этот вопрос превратился в вопрос в названии.)

Я пытался иметь 2 файла.

  • Первый находится в корневой папке приложения Jar.
  • Второй находится в каталоге, который указан в classpath.

2 файла называются application.properties.

Возможно ли объединить содержимое обоих файлов? (и значения свойств второго переопределяют свойства первого) Или, если у меня есть один файл, тогда другой файл игнорируется?

ОБНОВЛЕНИЕ 1: возможно "объединить" содержимое. Вчера казалось, что первый был проигнорирован, но кажется, что это что-то сломалось. Теперь это работает хорошо.

ОБНОВЛЕНИЕ 2: он вернулся снова! Опять же, только один из двух файлов применяется. Странно... Это началось после того, как я собрал файл jar приложения с помощью Spring Tool Suite. И кажется, что версия Jar всегда игнорирует вторую (в classpath), в то время как поведение расширенной версии, работающей на STS, варьируется. Откуда я могу начать расследование?

ОБНОВЛЕНИЕ 3:

Поведение версии Jar было на самом деле правильным. Это спецификация java.exe. Если указан параметр -jar, java.exe игнорирует параметр -classpath и переменную среды CLASSPATH, а путь к классам будет содержать только файл jar. Итак, второй файл application.properties на пути к классам игнорируется.

Теперь, как я могу позволить загружать второй application.properties в classpath?

ОБНОВЛЕНИЕ 4:

Мне удалось загрузить файл application.properties по внешнему пути, используя опцию -jar.

Ключ был PropertiesLauncher.

Чтобы использовать PropertiesLauncher, файл pom.xml должен быть изменен следующим образом:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>  <!-- added -->
                <layout>ZIP</layout> <!-- to use PropertiesLaunchar -->
            </configuration>
        </plugin>
    </plugins>
</build>

Для этого я сослался на следующий вопрос Stackru: средство запуска весенней загрузки не может быть использовано. Кстати, в документе Spring Boot Maven Plugin ( http://docs.spring.io/spring-boot/docs/1.1.7.RELEASE/maven-plugin/repackage-mojo.html) нет упоминания об указании триггеров ZIP что PropertiesLauncher используется. (Возможно в другом документе?)

После того, как файл jar был собран, я увидел, что PropertiesLauncher используется, проверив свойство Main-Class в META-INF/MENIFEST.MF в jar.

Теперь я могу запустить jar следующим образом (в Windows):

java -Dloader.path=file:///C:/My/External/Dir,MyApp-0.0.1-SNAPSHOT.jar -jar MyApp-0.0.1-SNAPSHOT.jar

Обратите внимание, что файл jar приложения включен в файл loader.path.

Теперь файл application.properties загружен в C:\My\External\Dir\config.

В качестве бонуса, любой файл (например, статический html-файл) в этом каталоге также может быть доступен для jar, поскольку он находится в пути к загрузчику.

Что касается не-jar (расширенной) версии, упомянутой в UPDATE 2, возможно, возникла проблема с порядком пути к классам.

(Кстати, я изменил название вопроса, чтобы быть более конкретным для этой проблемы.)

12 ответов

Решение

Мне удалось загрузить файл application.properties по внешнему пути, используя опцию -jar.

Ключ был PropertiesLauncher.

Чтобы использовать PropertiesLauncher, файл pom.xml должен быть изменен следующим образом:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>  <!-- added -->
                <layout>ZIP</layout> <!-- to use PropertiesLaunchar -->
            </configuration>
        </plugin>
    </plugins>
</build>

Для этого я сослался на следующий вопрос Stackru: средство запуска весенней загрузки не может быть использовано. Кстати, в документе Spring Boot Maven Plugin ( http://docs.spring.io/spring-boot/docs/1.1.7.RELEASE/maven-plugin/repackage-mojo.html) нет упоминания об указании триггеров ZIP что PropertiesLauncher используется. (Возможно в другом документе?)

После того, как файл jar был собран, я увидел, что PropertiesLauncher используется, проверив свойство Main-Class в META-INF/MENIFEST.MF в jar.

Теперь я могу запустить jar следующим образом (в Windows):

java -Dloader.path=file:///C:/My/External/Dir,MyApp-0.0.1-SNAPSHOT.jar -jar MyApp-0.0.1-SNAPSHOT.jar

Обратите внимание, что файл jar приложения включен в файл loader.path.

Теперь файл application.properties загружен в C:\My\External\Dir\config.

В качестве бонуса, любой файл (например, статический html-файл) в этом каталоге также может быть доступен для jar, поскольку он находится в пути к загрузчику.

Что касается не-jar (расширенной) версии, упомянутой в UPDATE 2, возможно, возникла проблема с порядком пути к классам.

Если вы не изменили настройки Spring Boot по умолчанию (это означает, что вы используете @EnableAutoConfiguration или же @SpringBootApplication и не изменили никакой обработки источника свойств), тогда он будет искать свойства в следующем порядке (наивысший переопределяет наименьший):

  1. /config Подкаталог текущего каталога
  2. Текущий каталог
  3. Классный путь /config пакет
  4. Корень пути к классам

Список выше упоминается в этой части документации

Это означает, что если свойство найдено, например, application.properties под src/resources будет переопределено свойством с тем же именем, найденным в application.properties в /config каталог, следующий за упакованным флягой.

Этот порядок по умолчанию, используемый Spring Boot, обеспечивает очень простую экстернализацию конфигурации, что, в свою очередь, упрощает настройку приложений в нескольких средах (dev, staging, production, cloud и т. Д.)

Чтобы увидеть весь набор функций, предоставляемых Spring Boot для чтения свойств (подсказка: доступно намного больше, чем чтение из application.properties) проверить эту часть документации.

Как видно из моего короткого описания выше или из полной документации, приложения Spring Boot очень удобны для DevOps!

Это все объясняется здесь, в документации:

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Что объясняет, что это порядок приоритета:

  • Подкаталог A / config текущего каталога.
  • Текущий каталог
  • Пакет classpath / config
  • Корень пути к классам

Это также указывает на то, что вы можете определить дополнительные файлы свойств для переопределений, например, так:

java -jar myproject.jar 
    --spring.config.location=classpath:/overrides.properties

Если вы используете spring.config.location, то все местоположения по умолчанию для application.properties также включены. Это означает, что вы можете установить значения по умолчанию в application.properties и переопределить, как требуется для конкретной среды.

Вы сможете запустить приложение весенней загрузки с путем к файлу внешних свойств следующим образом:

java -jar {jar-file-name}.jar 
--spring.config.location=file:///C:/{file-path}/{file-name}.properties
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layout>ZIP</layout> 
            </configuration>
        </plugin>
    </plugins>
</build>

java -Dloader.path=file:///absolute_path/external.jar -jar example.jar
java -jar server-0.0.1-SNAPSHOT.jar --spring.config.location=application-prod.properties

Я знаю, что это заостренный вопрос, и ОП хотел загрузить другой файл свойств.

Мой ответ таков: делать пользовательские хаки, как это, ужасная идея.

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

https://spring.io/projects/spring-cloud-config

Он загружает и объединяет определенные свойства по умолчанию / dev / project-default / project-dev, такие как magic

Опять же, загрузка Spring уже дает вам достаточно способов сделать это правильно https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Пожалуйста, не изобретайте велосипед заново.

Это может произойти в конце, но я думаю, что я нашел лучший способ загрузить внешние конфигурации, особенно когда вы запускаете приложение весенней загрузки, используя java jar myapp.war вместо @PropertySource("classpath:some.properties")

Конфигурация будет загружена из корня проекта или из места, из которого запускается файл war/jar

public class Application implements EnvironmentAware {

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

    @Override
    public void setEnvironment(Environment environment) {
        //Set up Relative path of Configuration directory/folder, should be at the root of the project or the same folder where the jar/war is placed or being run from
        String configFolder = "config";
        //All static property file names here
        List<String> propertyFiles = Arrays.asList("application.properties","server.properties");
        //This is also useful for appending the profile names
        Arrays.asList(environment.getActiveProfiles()).stream().forEach(environmentName -> propertyFiles.add(String.format("application-%s.properties", environmentName))); 
        for (String configFileName : propertyFiles) {
            File configFile = new File(configFolder, configFileName);
            LOGGER.info("\n\n\n\n");
            LOGGER.info(String.format("looking for configuration %s from %s", configFileName, configFolder));
            FileSystemResource springResource = new FileSystemResource(configFile);
            LOGGER.log(Level.INFO, "Config file : {0}", (configFile.exists() ? "FOund" : "Not Found"));
            if (configFile.exists()) {
                try {
                    LOGGER.info(String.format("Loading configuration file %s", configFileName));
                    PropertiesFactoryBean pfb = new PropertiesFactoryBean();
                    pfb.setFileEncoding("UTF-8");
                    pfb.setLocation(springResource);
                    pfb.afterPropertiesSet();
                    Properties properties = pfb.getObject();
                    PropertiesPropertySource externalConfig = new PropertiesPropertySource("externalConfig", properties);
                    ((ConfigurableEnvironment) environment).getPropertySources().addFirst(externalConfig);
                } catch (IOException ex) {
                    LOGGER.log(Level.SEVERE, null, ex);
                }
            } else {
                LOGGER.info(String.format("Cannot find Configuration file %s... \n\n\n\n", configFileName));

            }

        }
    }

}

Надеюсь, поможет.

Решение для файла yml:

1. Скопируйте yml в тот же каталог, в котором находится jar-приложение.

2. Запустите команду, пример для xxx.yml:

java -jar app.jar --spring.config.location=xxx.yml

Работает нормально, но при запуске логгера есть INFO:

No active profile set .........

Другой гибкий способ - использование classpath, содержащего толстый jar (-cp fat.jar) или всех jar (-cp "$JARS_DIR/*"), и другой настраиваемый путь к classpath или папку, содержащую файлы конфигурации, обычно в других местах и ​​за пределами jar. Поэтому вместо ограниченного java -jar используйте более гибкий путь к классам следующим образом:

java \
   -cp fat_app.jar \ 
   -Dloader.path=<path_to_your_additional_jars or config folder> \
   org.springframework.boot.loader.PropertiesLauncher

Смотрите Spring-boot исполняемый файл jar doc и эту ссылку

Если у вас есть несколько распространенных MainApps, вы можете использовать Как мне сказать Spring Boot, какой основной класс использовать для исполняемого файла jar?

Вы можете добавить дополнительные местоположения, установив переменную среды LOADER_PATH или loader.path в loader.properties (разделенный запятыми список каталогов, архивов или каталогов внутри архивов). В основном loader.path работает как для java -jar, так и для java -cp.

И, как всегда, вы можете переопределить и точно указать application.yml, который он должен использовать для отладки.

--spring.config.location=/some-location/application.yml --debug

Когда вы создаете весенний загрузочный jar с помощью maven install и хотите, чтобы все ресурсы, такие как файл свойств и папка lib, создавались вне jar ..., добавьте следующий код в pom.xml, где я определяю выходную папку, где я хочу извлечь и сохранить нужные ресурсы банку.

<build>
<finalName>project_name_Build/project_name</finalName>
   <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/project_name_Build/lib</outputDirectory>
                        <overWriteReleases>false</overWriteReleases>
                        <overWriteSnapshots>false</overWriteSnapshots>
                        <overWriteIfNewer>true</overWriteIfNewer>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>write here the qualified or complete path of main class of application</mainClass>
                    </manifest>
                    <manifestEntries>
                        <Class-Path>. resources/</Class-Path>
                    </manifestEntries>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>
    </plugins>

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>application.properties</include>
                <include>log4j.properties</include>
            </includes>
            <targetPath>${project.build.directory}/ConsentGatewayOfflineBuild/resources</targetPath>
        </resource>

        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>application.properties</include>
                <include>log4j.properties</include>
            </includes>
        </resource>

    </resources>

    <pluginManagement>
        <plugins>
            <!-- Ignore/Execute plugin execution -->
            <plugin>
                <groupId>org.eclipse.m2e</groupId>
                <artifactId>lifecycle-mapping</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <lifecycleMappingMetadata>
                        <pluginExecutions>
                            <!-- copy-dependency plugin -->
                            <pluginExecution>
                                <pluginExecutionFilter>
                                    <groupId>org.apache.maven.plugins</groupId>
                                    <artifactId>maven-dependency-plugin</artifactId>
                                    <versionRange>[1.0.0,)</versionRange>
                                    <goals>
                                        <goal>copy-dependencies</goal>
                                    </goals>
                                </pluginExecutionFilter>
                                <action>
                                    <ignore />
                                </action>
                            </pluginExecution>
                        </pluginExecutions>
                    </lifecycleMappingMetadata>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

Поместите свойства, которые вы хотите внедрить, в файл .properties или yml вне вашей банки.

Другие вопросы по тегам