Spring : Map 또는 Properties 객체로 모든 환경 속성에 액세스
주석을 사용하여 다음과 같이 스프링 환경을 구성합니다.
@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
. 이것이 제가하고 싶은 일입니다. 어떤 제안?
이와 같은 것이 필요합니다. 아마도 개선 될 수 있습니다. 이것은 첫 번째 시도입니다.
...
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
...
@Configuration
...
@org.springframework.context.annotation.PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer
{
@Autowired
Environment env;
public void someMethod() {
...
Map<String, Object> map = new HashMap();
for(Iterator it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
PropertySource propertySource = (PropertySource) it.next();
if (propertySource instanceof MapPropertySource) {
map.putAll(((MapPropertySource) propertySource).getSource());
}
}
...
}
...
기본적으로 환경의 모든 것 MapPropertySource
(그리고 상당히 많은 구현이 있음) Map
은 속성 으로 액세스 할 수 있습니다 .
이것은 오래된 질문이지만 받아 들여진 대답에는 심각한 결함이 있습니다. Spring Environment
객체가 ( Externalized Configuration에 설명 된대로) 재정의 값을 포함하는 경우 생성하는 속성 값의 맵이 Environment
객체 에서 반환 된 값과 일치 할 것이라는 보장이 없습니다 . 나는 단순히의 PropertySource
s를 반복하는 것이 Environment
실제로 어떤 재정의 값도 제공하지 않는다는 것을 발견했습니다 . 대신에 재정의되어야하는 원래 값을 생성했습니다.
여기에 더 나은 해결책이 있습니다. 이것은의 EnumerablePropertySource
s Environment
를 사용하여 알려진 속성 이름을 반복하지만 실제 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)));
키가 고유 한 접두어로 시작하는 모든 속성 (예 : "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() );
}
}
시작점은 포함 된 PropertySource를 반환 할 수있는 ConfigurableEnvironment입니다 (ConfigurableEnvironment는 Environment의 직계 하위 항목입니다). 다음과 같이 자동 배선 할 수 있습니다.
@Autowired
private ConfigurableEnvironment myEnv;
If you not using very special kinds of property sources (like JndiPropertySource, which is usually not used in spring autoconfiguration) you can retrieve all properties held in the environment.
The implementation relies on the iteration order which spring itself provides and takes the first found property, all later found properties with the same name are discarded. This should ensure the same behaviour as if the environment were asked directly for a property (returning the first found one).
Note also that the returned properties are not yet resolved if they contain aliases with the ${...} operator. If you want to have a particular key resolved you have to ask the Environment directly again:
myEnv.getProperty( key );
As this Spring's Jira ticket, it is an intentional design. But the following code works for me.
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;
}
The original question hinted that it would be nice to be able to filter all the properties based on a prefix. I have just confirmed that this works as of Spring Boot 2.1.1.RELEASE, for Properties
or Map<String,String>
. I'm sure it's worked for while now. Interestingly, it does not work without the prefix =
qualification, i.e. I do not know how to get the entire environment loaded into a map. As I said, this might actually be what OP wanted to begin with. The prefix and the following '.' will be stripped off, which might or might not be what one wants:
@ConfigurationProperties(prefix = "abc")
@Bean
public Properties getAsProperties() {
return new Properties();
}
@Bean
public MyService createService() {
Properties properties = getAsProperties();
return new MyService(properties);
}
Postscript: It is indeed possible, and shamefully easy, to get the entire environment. I don't know how this escaped me:
@ConfigurationProperties
@Bean
public Properties getProperties() {
return new Properties();
}
Spring won't allow to decouple via java.util.Properties
from Spring Environment.
But Properties.load()
still works in a Spring boot application:
Properties p = new Properties();
try (InputStream is = getClass().getResourceAsStream("/my.properties")) {
p.load(is);
}
The other answers have pointed out the solution for the majority of cases involving PropertySources
, but none have mentioned that certain property sources are unable to be casted into useful types.
One such example is the property source for command line arguments. The class that is used is SimpleCommandLinePropertySource
. This private class is returned by a public method, thus making it extremely tricky to access the data inside the object. I had to use reflection in order to read the data and eventually replace the property source.
If anyone out there has a better solution, I would really like to see it; however, this is the only hack I have gotten to work.
Working with Spring Boot 2, I needed to do something similar. Most of the answers above work fine, just beware that at various phases in the app lifecycles the results will be different.
For example, after a ApplicationEnvironmentPreparedEvent
any properties inside application.properties
are not present. However, after a ApplicationPreparedEvent
event they are.
For Spring Boot, the accepted answer will overwrite duplicate properties with lower priority ones. This solution will collect the properties into a SortedMap
and take only the highest priority duplicate properties.
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);
}
'program tip' 카테고리의 다른 글
Git : 푸시 후 커밋 된 파일 제거 (0) | 2020.11.17 |
---|---|
Java 7 경로의 확장을 확인하는 방법 (0) | 2020.11.17 |
PowerPivot, PowerQuery 및 PowerBI의 차이점 (0) | 2020.11.17 |
Ansible : 속성별로 목록 필터링 (0) | 2020.11.17 |
@RequestBody MultiValueMap에 대해 콘텐츠 유형 'application / x-www-form-urlencoded; charset = UTF-8'이 지원되지 않습니다. (0) | 2020.11.17 |