2015-09-30 4 views
1

Как я могу динамически получить доступ к оборудованию @Value во время выполнения?@Value at runtime

Я думал, что окружающая среда может быть то, что я искал, но

@Component 
public class SpringConfiguration implements ConfigurationI { 
    @Autowired 
    private Provider<Environment> env; 

    @Override 
    public String get(String key) { 
     try { 
      return env.get().getRequiredProperty(key); 
     } catch (IllegalStateException e) { 
      return null; 
     } 
    } 
} 

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

EDIT: пояснить мой пример использования. Это часть создания библиотеки с множеством частей, специфичных для весны (которые зависят от кучи старых весенних приложений), используемых в новых приложениях Guice путем переключения специальных аннотаций Spring для JSR 330 (javax.inject). Я надеялся избежать переписывания всего материала PropertyPlaceholderConfigurer во всех наших приложениях Spring, предоставляя приятную точку входа, подобную этой. Если есть еще один лучший способ сделать это (возможно, с @Named?), То я все уши.

EDIT2: Это (очищенный) пример того, какой тип PropertyPlaceholderConfigurer существует в приложениях, вызывающих эту библиотеку.

@Bean 
public PropertyPlaceholderConfigurer placeholderConfigurer() { 
    return new PropertyPlaceholderConfigurer() { 
     @Override 
     protected String resolvePlaceholder(String placeholder, Properties props) { 
      // Some code to parse and cleanup key here 
      String result = getPropertyFromLocalAppSpecificConfig(key); 
      if (result == null) { 
       result = super.resolvePlaceholder(placeholder, props); 
      } 
      // Some more random app specific logic for missing defaults 
      return result; 
     } 
    }; 
} 
+0

Как ваш 'PropertyPlaceholderConfigurer' настроен? –

+0

Я добавил пример на мой вопрос. Это инъекционный компонент. – kazagistar

ответ

0

PropertyPlaceholder и друзья не ставят свойства в ваших (в основном из-за обратной совместимости) Environment. Вместо этого они используют среду и собственный внутренний объект Properties, собранный в основном из файлов свойств из пути к классам для разрешения свойств @Value. Таким образом, свойства, загруженные из PropertyPlaceholder, не могут быть выбраны динамически (т.е. нет getProperty(String..)). Некоторые люди создают пользовательские PropertyPlaceholder, которые хранят свойства публично (через getter или что-то еще), но я думаю, что полностью побеждает новую конфигурацию конфигурации унифицированной среды Spring.

Что вы действительно хотите, вероятно, @PropertySource, который по-прежнему довольно дерьмовый, так как его не динамический (поскольку его аннотацию вы не можете изменить, откуда загружаются файлы), но он будет загружать свойства в Environment. Я имею в виду, чтобы проблемы с источником Spring о путанице этого.

В любом случае вы можете посмотреть на мое решение здесь: Manually add a @PropertySource: Configuring Environment before context is refreshed

В основном вам нужно раздобыть ConfigurableEnvironment и загружать свои свойства в него путем создания PropertySources. API для этого очень мощный, но не очень интуитивный. Вы можете использовать ApplicationContextInitializers, чтобы получить Environment, который имеет свои собственные раздражающие проблемы (см. Ссылку), или вы можете делать то, что я делаю ниже.

public class ConfigResourcesEnvironment implements 
    ResourceLoaderAware, EnvironmentAware, BeanDefinitionRegistryPostProcessor, EnvironmentPropertiesMapSupplier { 

    private Environment environment; 
    private Map<String, String> environmentPropertiesMap; 

    @Override 
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { 
     if (environment instanceof ConfigurableEnvironment) { 
      ConfigurableEnvironment env = ((ConfigurableEnvironment) this.environment); 
      List<PropertySource> propertySources; 
      try { 
       propertySources = loadPropertySources(); //Your custom method for propertysources 
      } catch (IOException e) { 
       throw new RuntimeException(e); 
      } 
      //Spring prefers primacy ordering so we reverse the order of the sources... You may not need to do this. 
      reverse(propertySources); 
      for (PropertySource rp : propertySources) { 
       env.getPropertySources().addLast(rp); 
      } 
      environmentPropertiesMap = ImmutableMap.copyOf(environmentPropertiesToMap(env)); 
     } 
     else { 
      environmentPropertiesMap = ImmutableMap.of(); 
     } 
    } 


    public static Map<String,String> environmentPropertiesToMap(ConfigurableEnvironment e) { 
     Map<String, String> properties = newLinkedHashMap(); 
     for (String n : propertyNames(e.getPropertySources())) { 
      String v = e.getProperty(n); 
      if (v != null) 
       properties.put(n, v); 
     } 
     return properties; 
    } 

    public static Iterable<String> propertyNames(PropertySources propertySources) { 
     LinkedHashSet<String> propertyNames = new LinkedHashSet<String>(); 
     for (PropertySource<?> p : propertySources) { 
      if (p instanceof EnumerablePropertySource) { 
       EnumerablePropertySource<?> e = (EnumerablePropertySource<?>) p; 
       propertyNames.addAll(asList(e.getPropertyNames())); 
      } 
     } 
     return propertyNames; 
    } 

    @Override 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
     //NOOP 
    } 

    @Override 
    public void setEnvironment(Environment environment) { 
     this.environment = environment; 
    } 


    public Map<String, String> getEnvironmentPropertiesMap() { 
     return environmentPropertiesMap; 
    } 

} 

После того как вы ConfigurableEnvironment загружен вы можете использовать интерфейс EnvironmentAware для вещей, которые нуждаются в окружающей среде или создать свой собственный интерфейс.

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

public interface EnvironmentPropertiesMapSupplier { 
    public Map<String, String> getEnvironmentPropertiesMap(); 

} 
+0

К сожалению, наши настройки конфигурации не хранятся в файле свойств. Помогает ли это мне, если мы их храним в чем-то другом? Должен ли я попытаться замять все мои файлы конфигурации в свойствах, чтобы весна могла их предоставить? – kazagistar

+0

Следовательно, вы не должны использовать '@ PropertySource', и именно о чем я жаловался. Посмотрите на мой другой вопрос (ссылка).Вам нужно создать «BeanDefinitionRegistryPostProcessor» и настроить «Среда», где вы будете выполнять загрузку своих свойств. –

+0

У нас есть «ApplicationContextInitializer», который извлекает свойства из нескольких местоположений (файлы, базы данных и т. Д.) И для каждого регистрирует «PropertySource». Очень хорошо работает, поскольку он прекрасно сочетается с Spring 'Environment' и, таким образом,' @ Value'. –