Необязательное местоположение @PropertySource

Я использую Spring 3.2 в веб-приложении, и я хотел бы иметь файл .properties в пути к классам, который содержит значения по умолчанию. Пользователь должен иметь возможность использовать JNDI для определения местоположения, где хранится другой .properties, который переопределяет значения по умолчанию.

Следующее работает до тех пор, пока пользователь установил свойство configLocation как JNDI.

@Configuration
@PropertySource({ "classpath:default.properties", "file:${java:comp/env/configLocation}/override.properties" })
public class AppConfig
{
}

Однако внешние переопределения должны быть необязательными, поэтому свойство JNDI.

В настоящее время я получаю исключение (java.io.FileNotFoundException: comp\env\configLocation\app.properties (The system cannot find the path specified), когда отсутствует свойство JNDI.

Как определить опциональный .properties, который используется только тогда, когда установлено свойство JNDI (configLocation)? Возможно ли это с помощью @PropertySource или есть другое решение?

Ответ 1

Попробуйте следующее. Создайте ApplicationContextInitializer

В веб-контексте: ApplicationContextInitializer<ConfigurableWebApplicationContext> и зарегистрируйте его в web.xml через:

<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>...ContextInitializer</param-value>
</context-param>

В ContextInitializer вы можете добавить свои файлы свойств через путь к классам и файловую систему (хотя и не пытались использовать JNDI).

  public void initialize(ConfigurableWebApplicationContext applicationContext) {
    String activeProfileName = null;
    String location = null;

    try {
      ConfigurableEnvironment environment = applicationContext.getEnvironment();
      String appconfigDir = environment.getProperty(APPCONFIG);
      if (appconfigDir == null ) {
        logger.error("missing property: " + APPCONFIG);
        appconfigDir = "/tmp";
      }
      String[] activeProfiles = environment.getActiveProfiles();

      for ( int i = 0; i < activeProfiles.length; i++ ) {
        activeProfileName = activeProfiles[i];
        MutablePropertySources propertySources = environment.getPropertySources();
        location = "file://" + appconfigDir + activeProfileName + ".properties";
        addPropertySource(applicationContext, activeProfileName,
                location, propertySources);
        location = "classpath:/" + activeProfileName + ".properties";
        addPropertySource(applicationContext, activeProfileName,
                          location, propertySources);
      }
      logger.debug("environment: '{}'", environment.getProperty("env"));

    } catch (IOException e) {
      logger.info("could not find properties file for active Spring profile '{}' (tried '{}')", activeProfileName, location);
      e.printStackTrace();
    }
  }

  private void addPropertySource(ConfigurableWebApplicationContext applicationContext, String activeProfileName,
                                 String location, MutablePropertySources propertySources) throws IOException {
    Resource resource = applicationContext.getResource(location);
    if ( resource.exists() ) {
      ResourcePropertySource propertySource = new ResourcePropertySource(location);
      propertySources.addLast(propertySource);
    } else {
      logger.info("could not find properties file for active Spring profile '{}' (tried '{}')", activeProfileName, location);
    }
  }

В приведенном выше коде пытается найти файл свойств для активного профиля (см. Как установить активный профиль среды spring 3.1 через файл с правами, а не через переменную env или систему свойство)

Ответ 2

По состоянию на Spring 4 проблема SPR-8371 решена. Следовательно, аннотация @PropertySource имеет новый атрибут ignoreResourceNotFound, который был добавлен именно для этой цели. Кроме того, есть также новая аннотация @PropertySources, которая позволяет такие реализации, как:

@PropertySources({
    @PropertySource("classpath:default.properties"),
    @PropertySource(value = "file:/path_to_file/optional_override.properties", ignoreResourceNotFound = true)
})

Ответ 3

Если вы еще не на Spring 4 (см. решение matsev), здесь более подробное, но примерно эквивалентное решение:

@Configuration
@PropertySource("classpath:default.properties")
public class AppConfig {

    @Autowired
    public void addOptionalProperties(StandardEnvironment environment) {
        try {
            String localPropertiesPath = environment.resolvePlaceholders("file:${java:comp/env/configLocation}/override.properties");
            ResourcePropertySource localPropertySource = new ResourcePropertySource(localPropertiesPath);
            environment.getPropertySources().addLast(localPropertySource);
        } catch (IOException ignored) {}
    }

}