복붙노트

[SPRING] Spring : 모든 환경 속성에 Map 또는 Properties 객체로 액세스

SPRING

Spring : 모든 환경 속성에 Map 또는 Properties 객체로 액세스

주석을 사용하여 다음과 같이 스프링 환경을 구성합니다.

@Configuration
...
@PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer 
{
    @Autowired
    Environment env;
}

이것은 환경의 일부인 default.properties의 속성으로 연결됩니다. 환경 설정 (예 : config_dir 위치)에 따라 여러 대체 시스템 계층과 다양한 동적 위치를 통해 속성을 오버로드 할 수있는 기능을 이미 제공하고 있으므로 여기 @PropertySource 메커니즘을 사용하고 싶습니다. 방금 예제를 쉽게 만들기 위해 폴백을 제거했습니다.

그러나 이제는 내 문제는 예를 들어 default.properties에 데이터 소스 속성을 구성하려고합니다. 데이터 소스가 사용할 것으로 예상되는 설정을 자세히 알지 못해도 설정을 데이터 소스에 전달할 수 있습니다.

Properties p = ...
datasource.setProperties(p);

그러나 문제는 Environment 객체가 Properties 객체도 Map도 아니며 비교할 수없는 객체라는 점입니다. 필자가 생각하기에 환경의 모든 값에 접근하는 것은 불가능하다. 왜냐하면 keySet이나 iterator 메소드가 없기 때문이다.

Properties p <=== Environment env?

내가 놓친 게 있니? 어떻게 든 Environment 객체의 모든 항목에 액세스 할 수 있습니까? 그렇다면 엔트리를 Map 또는 Properties 객체에 매핑 할 수 있습니다. 접두어로 필터하거나 매핑 할 수도 있습니다 - 하위 세트를 표준 Java Map으로 만듭니다 ... 이것은 내가하고 싶은 것입니다. 어떤 제안?

해결법

  1. ==============================

    1.당신은 이것과 같은 것이 필요합니다. 아마 이것은 개선 될 수 있습니다. 이것이 첫 번째 시도입니다 :

    당신은 이것과 같은 것이 필요합니다. 아마 이것은 개선 될 수 있습니다. 이것이 첫 번째 시도입니다 :

    ...
    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 인 환경 (및 구현이 상당히 많음)의 모든 것을 특성 맵으로 액세스 할 수 있습니다.

  2. ==============================

    2.이것은 오래된 질문이지만, 받아 들여진 대답은 심각한 결함이 있습니다. Spring Environment 객체에 오버 라이딩 된 값이 포함되어있는 경우 (Externalized Configuration에 설명 된대로) 생성 된 속성 값의 맵이 Environment 객체에서 반환 된 값과 일치한다는 보장이 없습니다. 환경의 PropertySources를 반복하는 것만으로도 실제로 어떤 중요한 값도 제공하지 않는다는 것을 알았습니다. 대신 원래 값, 즉 재정의되어야 할 값을 생성했습니다.

    이것은 오래된 질문이지만, 받아 들여진 대답은 심각한 결함이 있습니다. Spring Environment 객체에 오버 라이딩 된 값이 포함되어있는 경우 (Externalized Configuration에 설명 된대로) 생성 된 속성 값의 맵이 Environment 객체에서 반환 된 값과 일치한다는 보장이 없습니다. 환경의 PropertySources를 반복하는 것만으로도 실제로 어떤 중요한 값도 제공하지 않는다는 것을 알았습니다. 대신 원래 값, 즉 재정의되어야 할 값을 생성했습니다.

    여기에 더 나은 해결책이 있습니다. 환경의 EnumerablePropertySources를 사용하여 알려진 속성 이름을 반복하지만 실제 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)));
    
  3. ==============================

    3.키가 별개의 접두사 (예 : "log4j.appender"로 시작하는 모든 속성)로 시작하는 모든 속성을 검색해야하고 다음 코드 (Java 8의 스트림과 lamdas 사용)를 작성해야합니다.

    키가 별개의 접두사 (예 : "log4j.appender"로 시작하는 모든 속성)로 시작하는 모든 속성을 검색해야하고 다음 코드 (Java 8의 스트림과 lamdas 사용)를 작성해야합니다.

    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() );
        }
    }
    

    시작 지점은 포함 된 PropertySources를 반환 할 수있는 ConfigurableEnvironment입니다 (ConfigurableEnvironment는 환경의 직계 하위 항목 임). 다음과 같이 autowire 할 수 있습니다.

    @Autowired
    private ConfigurableEnvironment  myEnv;
    

    매우 특별한 종류의 속성 소스 (예 : JndiPropertySource)를 사용하지 않는 경우 (일반적으로 봄 자동 구성에서는 사용되지 않음) 환경에 포함 된 모든 속성을 검색 할 수 있습니다.

    구현은 스프링 자체가 제공하고 처음 발견 된 속성을 취하는 반복 순서에 의존하며 나중에 발견 된 동일한 이름의 속성은 모두 삭제됩니다. 이것은 환경이 속성에 대해 직접 요청 된 것과 동일한 동작을 보장해야합니다 (발견 된 첫 번째 반환).

    또한 $ {...} 연산자로 별칭이 포함 된 경우 반환 된 속성은 아직 해결되지 않았습니다. 특정 키를 확인하려면 환경에 다시 질문해야합니다.

    myEnv.getProperty( key );
    
  4. ==============================

    4.이 봄의 Jira 티켓으로, 이는 의도적 인 디자인입니다. 그러나 다음 코드는 나를 위해 작동합니다.

    이 봄의 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;
    }
    
  5. ==============================

    5.다른 답변은 PropertySources를 포함하는 대부분의 경우에 대한 해결책을 지적했지만, 특정 속성 소스를 유용한 유형으로 형변환 할 수 없다는 언급은 아직 없습니다.

    다른 답변은 PropertySources를 포함하는 대부분의 경우에 대한 해결책을 지적했지만, 특정 속성 소스를 유용한 유형으로 형변환 할 수 없다는 언급은 아직 없습니다.

    그러한 예제 중 하나는 명령 줄 인수의 속성 소스입니다. 사용되는 클래스는 SimpleCommandLinePropertySource입니다. 이 private 클래스는 public 메서드에 의해 반환되므로 객체 내부의 데이터에 액세스하는 것은 매우 까다로운 작업입니다. 데이터를 읽고 궁극적으로 속성 소스를 바꾸려면 리플렉션을 사용해야했습니다.

    더 나은 해결책을 가진 사람이 있다면 정말보고 싶습니다. 그러나 이것이 내가 일한 유일한 해킹입니다.

  6. ==============================

    6.다음 코드를 시도해보십시오.

    다음 코드를 시도해보십시오.

    ...
    @Autowired
    private Environment env;
    ...
    for(Iterator<PropertySource<?>> it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
        PropertySource<?> propertySource = (PropertySource<?>) it.next();
        if (propertySource instanceof CompositePropertySource) {
            for(Iterator<PropertySource<?>> it2 = ((CompositePropertySource) propertySource).getPropertySources().iterator(); it2.hasNext(); ) {
                PropertySource<?> propertySource2 = (PropertySource<?>) it2.next();
                if (propertySource2 instanceof ResourcePropertySource) {
                    for (Entry<String, Object> entry : ((ResourcePropertySource)propertySource2).getSource().entrySet()) {
                        if (entry.getValue() instanceof String) { 
                            System.out.println(entry.getKey() + "=" + (String)entry.getValue());
                        }
                    }
                }
            }
        }
    }
    
  7. ==============================

    7.스프링 부트 2로 작업하면서 비슷한 것을해야했습니다. 위 답변의 대부분은 정상적으로 작동하지만 앱 라이프 사이클의 여러 단계에서 결과가 달라질 수 있음을 명심하십시오.

    스프링 부트 2로 작업하면서 비슷한 것을해야했습니다. 위 답변의 대부분은 정상적으로 작동하지만 앱 라이프 사이클의 여러 단계에서 결과가 달라질 수 있음을 명심하십시오.

    예를 들어, ApplicationEnvironmentPreparedEvent 다음에 application.properties 내의 모든 특성이 없습니다. 그러나 ApplicationPreparedEvent 이벤트가 발생하면 이벤트가 발생합니다.

  8. ==============================

    8.Spring은 Spring Environment로부터 java.util.Properties를 통해 분리를 허용하지 않습니다.

    Spring은 Spring Environment로부터 java.util.Properties를 통해 분리를 허용하지 않습니다.

    그러나 Properties.load ()는 여전히 스프링 부트 응용 프로그램에서 작동합니다.

    Properties p = new Properties();
    try (InputStream is = getClass().getResourceAsStream("/my.properties")) {
        p.load(is);
    }
    
  9. from https://stackoverflow.com/questions/23506471/spring-access-all-environment-properties-as-a-map-or-properties-object by cc-by-sa and MIT license