Я использую аннотации для настройки моей среды Spring следующим образом:
@Configuration
...
@PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer
{
@Autowired
Environment env;
}
Это приводит к тому, что мои свойства default.properties
являются частью Environment
. Я хочу использовать этот @PropertySource
механизм здесь, потому что он уже предоставляет возможность перегрузки свойств через несколько резервных уровней и различные динамические местоположения, в зависимости от настроек среды (например, расположение config_dir). Я просто убрал запасной вариант, чтобы упростить пример.
Однако сейчас моя проблема в том, что я хочу настроить, например, свои свойства источника данных в default.properties
. Вы можете передать настройки источнику данных, не зная подробно, какие настройки ожидает источник данных, используя
Properties p = ...
datasource.setProperties(p);
Однако проблема в том, что Environment
объект не является ни Properties
объектом, Map
ни чем-либо сопоставимым. С моей точки зрения, просто невозможно получить доступ ко всем значениям среды, потому что нет keySet
ни iterator
метода, ни чего-либо сопоставимого.
Properties p <=== Environment env?
Я что-то упускаю? Можно ли Environment
каким-то образом получить доступ ко всем записям объекта? Если да, я мог бы сопоставить записи с объектом Map
или Properties
, я мог бы даже отфильтровать или сопоставить их по префиксу - создать подмножества как стандартную java Map
... Это то, что я хотел бы сделать. Какие-либо предложения?
applicationContext.getEnvironment().getProperty(key)
для их разрешенияЭто старый вопрос, но принятый ответ имеет серьезный недостаток. Если
Environment
объект Spring содержит какие-либо замещающие значения (как описано в разделе « Внешняя конфигурация» ), нет гарантии, что созданная им карта значений свойств будет соответствовать значениям, возвращаемым изEnvironment
объекта. Я обнаружил, что просто перебираяPropertySource
s изEnvironment
действительности не дает никаких переопределяющих значений. Вместо этого было получено исходное значение, то есть то, которое следовало изменить.Вот лучшее решение. Он использует
EnumerablePropertySource
sEnvironment
для перебора известных имен свойств, но затем считывает фактическое значение из реальной среды Spring. Это гарантирует, что это значение действительно разрешено Spring, включая любые замещающие значения.Properties props = new Properties(); MutablePropertySources propSrcs = ((AbstractEnvironment) springEnv).getPropertySources(); StreamSupport.stream(propSrcs.spliterator(), false) .filter(ps -> ps instanceof EnumerablePropertySource) .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) .flatMap(Arrays::<String>stream) .forEach(propName -> props.setProperty(propName, springEnv.getProperty(propName)));
источник
collect
метод на потоке вместо того , чтобы делатьforEach
:.distinct().collect(Collectors.toMap(Function.identity(), springEnv::getProperty))
. Если вам нужно было собрать его в Properties вместо Map, вы можете использовать версию с четырьмя аргументамиcollect
.springEnv
? Откуда это взялось? Отличается ли оно отenv
принятого решения?springEnv
являетсяenv
предметом исходного вопроса и принятого решения. Полагаю, мне следовало оставить имя без изменений.ConfigurableEnvironment
и не должны делать гипс.У меня было требование получить все свойства, ключ которых начинается с отдельного префикса (например, все свойства, начинающиеся с «log4j.appender.»), И я написал следующий код (используя потоки и ламды Java 8).
public static Map<String,Object> getPropertiesStartingWith( ConfigurableEnvironment aEnv, String aKeyPrefix ) { Map<String,Object> result = new HashMap<>(); Map<String,Object> map = getAllProperties( aEnv ); for (Entry<String, Object> entry : map.entrySet()) { String key = entry.getKey(); if ( key.startsWith( aKeyPrefix ) ) { result.put( key, entry.getValue() ); } } return result; } public static Map<String,Object> getAllProperties( ConfigurableEnvironment aEnv ) { Map<String,Object> result = new HashMap<>(); aEnv.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) ); return result; } public static Map<String,Object> getAllProperties( PropertySource<?> aPropSource ) { Map<String,Object> result = new HashMap<>(); if ( aPropSource instanceof CompositePropertySource) { CompositePropertySource cps = (CompositePropertySource) aPropSource; cps.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) ); return result; } if ( aPropSource instanceof EnumerablePropertySource<?> ) { EnumerablePropertySource<?> ps = (EnumerablePropertySource<?>) aPropSource; Arrays.asList( ps.getPropertyNames() ).forEach( key -> result.put( key, ps.getProperty( key ) ) ); return result; } // note: Most descendants of PropertySource are EnumerablePropertySource. There are some // few others like JndiPropertySource or StubPropertySource myLog.debug( "Given PropertySource is instanceof " + aPropSource.getClass().getName() + " and cannot be iterated" ); return result; } private static void addAll( Map<String, Object> aBase, Map<String, Object> aToBeAdded ) { for (Entry<String, Object> entry : aToBeAdded.entrySet()) { if ( aBase.containsKey( entry.getKey() ) ) { continue; } aBase.put( entry.getKey(), entry.getValue() ); } }
Обратите внимание, что отправной точкой является ConfigurableEnvironment, которая может возвращать встроенные PropertySources (ConfigurableEnvironment является прямым потомком Environment). Вы можете выполнить автоматическое подключение:
@Autowired private ConfigurableEnvironment myEnv;
Если вы не используете особые типы источников свойств (например, JndiPropertySource, который обычно не используется в автоконфигурации Spring), вы можете получить все свойства, хранящиеся в среде.
Реализация зависит от порядка итераций, который предоставляет сама пружина, и принимает первое найденное свойство, все последующие найденные свойства с тем же именем отбрасываются. Это должно гарантировать такое же поведение, как если бы окружение запрашивало свойство напрямую (возвращая первое найденное свойство).
Также обратите внимание, что возвращенные свойства еще не разрешены, если они содержат псевдонимы с оператором $ {...}. Если вы хотите разрешить определенный ключ, вам нужно снова напрямую спросить Environment:
myEnv.getProperty( key );
источник
NullPointerException
в своих модульных тестах, когда он пытается получить@Autowired
экземплярConfigurationEnvironment
.Исходный вопрос намекал, что было бы неплохо иметь возможность фильтровать все свойства на основе префикса. Я только что подтвердил, что это работает с Spring Boot 2.1.1.RELEASE для
Properties
илиMap<String,String>
. Я уверен, что пока это работает. Интересно, что это не работает безprefix =
квалификации, т.е. я не знаю, как загрузить все окружение на карту. Как я уже сказал, это могло быть именно то, с чего OP хотел начать. Префикс и следующий за ним "." будет снято, что может быть, а может и не быть тем, что нужно:@ConfigurationProperties(prefix = "abc") @Bean public Properties getAsProperties() { return new Properties(); } @Bean public MyService createService() { Properties properties = getAsProperties(); return new MyService(properties); }
Постскриптум: действительно возможно и позорно легко получить всю окружающую среду. Не знаю, как это от меня сбежало:
@ConfigurationProperties @Bean public Properties getProperties() { return new Properties(); }
источник
getAsProperties()
всегда возвращается пустойProperties
экземпляр, а попытка его без указанного префикса даже не позволяет ему скомпилировать. Это с Spring Boot 2.1.6.RELEASEКак и этот весенний билет Jira , это намеренный дизайн. Но у меня работает следующий код.
public static Map<String, Object> getAllKnownProperties(Environment env) { Map<String, Object> rtn = new HashMap<>(); if (env instanceof ConfigurableEnvironment) { for (PropertySource<?> propertySource : ((ConfigurableEnvironment) env).getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { rtn.put(key, propertySource.getProperty(key)); } } } } return rtn; }
источник
Spring не позволяет отделиться
java.util.Properties
от среды Spring.Но
Properties.load()
все еще работает в приложении загрузки Spring:Properties p = new Properties(); try (InputStream is = getClass().getResourceAsStream("/my.properties")) { p.load(is); }
источник
В других ответах указано решение для большинства связанных с этим случаев
PropertySources
, но ни один из них не упомянул, что определенные источники свойств не могут быть преобразованы в полезные типы.Одним из таких примеров является источник свойств для аргументов командной строки. Используемый класс
SimpleCommandLinePropertySource
. Этот частный класс возвращается общедоступным методом, что затрудняет доступ к данным внутри объекта. Мне пришлось использовать отражение, чтобы прочитать данные и в конечном итоге заменить источник свойства.Если у кого-то есть лучшее решение, я бы очень хотел его увидеть; однако это единственный хитрость, с которой мне пришлось работать.
источник
Работая с Spring Boot 2, мне нужно было сделать нечто подобное. Большинство приведенных выше ответов работают нормально, просто помните, что на разных этапах жизненного цикла приложения результаты будут разными.
Например, после a
ApplicationEnvironmentPreparedEvent
никаких свойств внутриapplication.properties
нет. Однако послеApplicationPreparedEvent
события они есть.источник
Для Spring Boot принятый ответ перезапишет повторяющиеся свойства с более низким приоритетом . Это решение будет собирать свойства в
SortedMap
и принимать только повторяющиеся свойства с наивысшим приоритетом.final SortedMap<String, String> sortedMap = new TreeMap<>(); for (final PropertySource<?> propertySource : env.getPropertySources()) { if (!(propertySource instanceof EnumerablePropertySource)) continue; for (final String name : ((EnumerablePropertySource<?>) propertySource).getPropertyNames()) sortedMap.computeIfAbsent(name, propertySource::getProperty); }
источник
Я решил добавить еще один способ. В моем случае я предоставляю это,
com.hazelcast.config.XmlConfigBuilder
которому нужно толькоjava.util.Properties
разрешить некоторые свойства внутри файла конфигурации Hazelcast XML, т.е. он вызывает толькоgetProperty(String)
метод. Итак, это позволило мне сделать то, что мне было нужно:@RequiredArgsConstructor public class SpringReadOnlyProperties extends Properties { private final org.springframework.core.env.Environment delegate; @Override public String getProperty(String key) { return delegate.getProperty(key); } @Override public String getProperty(String key, String defaultValue) { return delegate.getProperty(key, defaultValue); } @Override public synchronized String toString() { return getClass().getName() + "{" + delegate + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SpringReadOnlyProperties that = (SpringReadOnlyProperties) o; return delegate.equals(that.delegate); } @Override public int hashCode() { return Objects.hash(super.hashCode(), delegate); } private void throwException() { throw new RuntimeException("This method is not supported"); } //all methods below throw the exception * override all methods * }
PS Я закончил тем, что не использовал это специально для Hazelcast, потому что он разрешает только свойства для файла XML, но не во время выполнения. Так как я тоже использую Spring, я решил использовать custom
org.springframework.cache.interceptor.AbstractCacheResolver#getCacheNames
. Это разрешает свойства для обеих ситуаций, по крайней мере, если вы используете свойства в именах кешей.источник