2013-01-19 21 views
7

私は、Configという名前のSpringクラスのアプリケーションライブラリで作業しています。このクラスは、SpringのEnvironmentオブジェクトに基づいており、すべてのアプリケーション設定値に対して強く型付けされたgetterを提供します。春の環境プロパティーのソース設定

設定のプロパティソースは、環境(DEV/PROD)と使用方法(スタンドアロン/テスト/ Webアプリケーション)によって異なり、デフォルトのもの(システム& env props)からカスタムデータベースとJNDIソースまでです。私は苦労してる何

は、このライブラリを消費するアプリが簡単に性質が私たちのConfigクラスにしてPropertySourcesPlaceholderConfigurerを経由して使用可能であるように、Environmentで使用されるプロパティのソース(S)を設定できるようにする方法です。

私たちはまだXML構成を使用していますので、理想的にはこれはXMLで設定することができます。

<bean id="propertySources" class="..."> 
    <property name="sources"> 
     <list> 
      <ref local="jndiPropertySource"/> 
      <ref local="databasePropertySource"/> 
     </list> 
    </property> 
</bean> 

...環境のプロパティーソースコレクションに何らかの形で注入します。

私は、このようなことは、アプリケーションのコンテキストライフサイクルのタイミングにより不可能であり、アプリケーション初期化子クラスを使用して行う必要があることを読んだことがあります。

アイデア?

答えて

7

私はうまくいくように思えますが、私はかなり新しいですが、私はそれがどのように異なる用途ケース。

基本的には、PropertySourcesPlaceholderConfigurerを拡張し、ユーザーがXMLのPropertySourceオブジェクトのリストを簡単に設定できるようにするための設定を追加することです。作成後、プロパティソースは現在のEnvironmentにコピーされます。

これは基本的にプロパティーソースを1か所で構成できますが、placholder構成とEnvironment.getPropertyシナリオの両方で使用できます。

拡張PropertySourcesPlaceholderConfigurer

public class ConfigSourcesConfigurer 
     extends PropertySourcesPlaceholderConfigurer 
     implements EnvironmentAware, InitializingBean { 

    private Environment environment; 
    private List<PropertySource> sourceList; 

    // Allow setting property sources as a List for easier XML configuration 
    public void setPropertySources(List<PropertySource> propertySources) { 

     this.sourceList = propertySources; 
     MutablePropertySources sources = new MutablePropertySources(); 
     copyListToPropertySources(this.sourceList, sources);   
     super.setPropertySources(sources); 
    } 

    @Override 
    public void setEnvironment(Environment environment) { 
     // save off Environment for later use 
     this.environment = environment; 
     super.setEnvironment(environment); 
    } 

    @Override 
    public void afterPropertiesSet() throws Exception { 

     // Copy property sources to Environment 
     MutablePropertySources envPropSources = ((ConfigurableEnvironment)environment).getPropertySources(); 
     copyListToPropertySources(this.sourceList, envPropSources); 
    } 

    private void copyListToPropertySources(List<PropertySource> list, MutablePropertySources sources) { 

     // iterate in reverse order to insure ordering in property sources object 
     for(int i = list.size() - 1; i >= 0; i--) { 
      sources.addFirst(list.get(i)); 
     } 
    } 
} 

豆。xmlファイルの基本設定を表示

<beans> 
    <context:annotation-config/> 
    <context:component-scan base-package="com.mycompany" /> 

    <bean class="com.mycompany.ConfigSourcesConfigurer"> 
     <property name="propertySources"> 
      <list> 
       <bean class="org.mycompany.CustomPropertySource" /> 
       <bean class="org.springframework.core.io.support.ResourcePropertySource"> 
        <constructor-arg value="classpath:default-config.properties" /> 
       </bean> 
      </list> 
     </property> 
    </bean> 
    <bean class="com.mycompany.TestBean"> 
     <property name="stringValue" value="${placeholder}" /> 
    </bean> 
</beans> 
+1

CustomPropertySourceの外観の例を挙げることはできますか? – naumcho

11

${propertyname}構文を使用してプロパティを注入する場合、プロパティをどのように使用するかによって異なります。次に、PropertySourcesPlaceHolderConfigurerが動作するだけで、内部的に環境に登録されているPropertySourcesにアクセスできます。

Environmentを直接使用する場合は、env.getProperty()と言ってください。あなたが正しいです。PropertySourcesPlaceHolderConfigurerを使用するプロパティはここでは表示されません。唯一の方法は、Javaコードを使用して注入することです。私が知っている2つの方法があります。

a。 Java Configを使用する:

@Configuration 
@PropertySource("classpath:/app.properties") 
public class SpringConfig{ 

} 

b。カスタムを使用するApplicationContextInitializer、それは記載されている方法here

+0

私たちは両方のケース(環境直接とplacholderの設定者)が必要です。しかしこれをもう少し見てみると、他のソースが指定されていなければ、プレースホルダconfigが環境に延期されるという点で少し統一されているように見えます。それが本当であれば、環境上でソースを設定するだけで済むので、プレースホルダー設定ツールで「無料」で取得できます。だから、私たちは@PropertySourceアノテーションのようなものが必要だが、Javaの代わりにXMLで設定できるようだ。そんなことは何ですか? – WayneC

+0

もう一度質問します。私は 'ApplicationContextInitializer'の例で、環境に追加される' PropertySource'がコンフィグレーションされたBeanである必要がある場合(例えばデータベース接続情報が必要です)どうでしょうか?アプリケーションコンテキストが初期化されているため、Beanはまだ使用できません。鶏肉/卵の問題のようです。 – WayneC

+0

はい、私は同意し、非常にきれいに感じることはありません - 個人的に@PropertySourceを使用して私のために非常にうまくいって、xmlに相当するように見えません。 –

2

以下は、Spring 3.2.4で私にとって役立ちました。

PropertySourcesPlaceholderConfigurerは、プレースホルダを処理するために静的に登録する必要があります。

カスタムプロパティソースはinitメソッドに登録されています。既定のプロパティソースは既に登録されているため、プレースホルダを使用してパラメータ化できます。

JavaConfigクラス:

@Configuration 
@PropertySource("classpath:propertiesTest2.properties") 
public class TestConfig { 

    @Autowired 
    private ConfigurableEnvironment env; 

    @Value("${param:NOVALUE}") 
    private String param; 

    @PostConstruct 
    public void init() { 
     env.getPropertySources().addFirst(new CustomPropertySource(param)); 
    } 

    @Bean 
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() { 
     return new PropertySourcesPlaceholderConfigurer(); 
    } 

    @Bean 
    public TestBean1 testBean1() { 
     return new TestBean1(); 
    } 
} 

カスタムプロパティソース:

public class CustomPropertySource extends PropertySource<Object> { 

    public CustomPropertySource(String param) { 
     super("custom"); 
     System.out.println("Custom property source initialized with param " + param + "."); 
    } 

    @Override 
    public Object getProperty(String name) { 
     return "IT WORKS"; 
    } 

} 

テスト豆(getValue()意志出力"IT WORKS"):

public class TestBean1 { 

    @Value("${value:NOVALUE}") 
    private String value; 

    public String getValue() { 
     return value; 
    } 
} 
0

私は最近、どのように問題に遭遇しました環境内のカスタムプロパティソースを登録します。私の特定の問題は、SpringアプリケーションコンテキストにインポートしたいSpring構成のライブラリがあり、カスタムのプロパティソースが必要だということです。ただし、アプリケーションコンテキストが作成される場所をすべて管理する必要はありません。このため、カスタムプロパティソースを登録するために、推奨されるApplicationContextInitializerまたはregister-before-refreshのメカニズムを使用することは望ましくありません。

私が本当にイライラしていたのは、古いPropertyPlaceholderConfigurerを使用すると、Springの設定内でコンフィグレーターを完全にサブクラス化してカスタマイズできることでした。対照的に、プロパティー・ソースをカスタマイズするには、Springの設定自体ではなく、アプリケーション・コンテキストが初期化される前に行う必要があると言われています。

いくつかの調査と試行錯誤の末、私は、であることを発見しました。これはSpring構成の内部からカスタムプロパティーソースを登録することが可能ですが、どのように行うか注意する必要があります。 PropertySourcesPlaceholderConfigurersがコンテキストで実行される前に、ソースを登録する必要があります。これを行うには、ソース登録をPriorityOrderedのBeanFactoryPostProcessorにし、ソースを使用するPropertySourcesPlaceholderConfigurerより優先順位の高いオーダーを作成します。私は仕事をして、このクラス、書いた

:ここで注目すべき

package example; 

import java.io.IOException; 
import java.util.Properties; 

import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.BeanInitializationException; 
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 
import org.springframework.core.Ordered; 
import org.springframework.core.PriorityOrdered; 
import org.springframework.core.env.ConfigurableEnvironment; 
import org.springframework.core.env.Environment; 
import org.springframework.core.env.MutablePropertySources; 
import org.springframework.core.env.PropertiesPropertySource; 
import org.springframework.core.env.PropertySource; 
import org.springframework.core.io.support.PropertiesLoaderSupport; 

/** 
* This is an abstract base class that can be extended by any class that wishes 
* to become a custom property source in the Spring context. 
* <p> 
* This extends from the standard Spring class PropertiesLoaderSupport, which 
* contains properties that specify property resource locations, plus methods 
* for loading properties from specified resources. These are all available to 
* be used from the Spring configuration, and by subclasses of this class. 
* <p> 
* This also implements a number of Spring flag interfaces, all of which are 
* required to maneuver instances of this class into a position where they can 
* register their property sources BEFORE PropertySourcesPlaceholderConfigurer 
* executes to substitute variables in the Spring configuration: 
* <ul> 
* <li>BeanFactoryPostProcessor - Guarantees that this bean will be instantiated 
* before other beans in the context. It also puts it in the same phase as 
* PropertySourcesPlaceholderConfigurer, which is also a BFPP. The 
* postProcessBeanFactory method is used to register the property source.</li> 
* <li>PriorityOrdered - Allows the bean priority to be specified relative to 
* PropertySourcesPlaceholderConfigurer so that this bean can be executed first. 
* </li> 
* <li>ApplicationContextAware - Provides access to the application context and 
* its environment so that the created property source can be registered.</li> 
* </ul> 
* <p> 
* The Spring configuration for subclasses should contain the following 
* properties: 
* <ul> 
* <li>propertySourceName - The name of the property source this will register.</li> 
* <li>location(s) - The location from which properties will be loaded.</li> 
* <li>addBeforeSourceName (optional) - If specified, the resulting property 
* source will be added before the given property source name, and will 
* therefore take precedence.</li> 
* <li>order (optional) - The order in which this source should be executed 
* relative to other BeanFactoryPostProcessors. This should be used in 
* conjunction with addBeforeName so that if property source factory "psfa" 
* needs to register its property source before the one from "psfb", "psfa" 
* executes AFTER "psfb". 
* </ul> 
* 
* @author rjsmith2 
* 
*/ 
public abstract class AbstractPropertySourceFactory extends 
     PropertiesLoaderSupport implements ApplicationContextAware, 
     PriorityOrdered, BeanFactoryPostProcessor { 

    // Default order will be barely higher than the default for 
    // PropertySourcesPlaceholderConfigurer. 
    private int order = Ordered.LOWEST_PRECEDENCE - 1; 

    private String propertySourceName; 

    private String addBeforeSourceName; 

    private ApplicationContext applicationContext; 

    private MutablePropertySources getPropertySources() { 
     final Environment env = applicationContext.getEnvironment(); 
     if (!(env instanceof ConfigurableEnvironment)) { 
      throw new IllegalStateException(
        "Cannot get environment for Spring application context"); 
     } 
     return ((ConfigurableEnvironment) env).getPropertySources(); 
    } 

    public int getOrder() { 
     return order; 
    } 

    public void setOrder(int order) { 
     this.order = order; 
    } 

    public String getPropertySourceName() { 
     return propertySourceName; 
    } 

    public void setPropertySourceName(String propertySourceName) { 
     this.propertySourceName = propertySourceName; 
    } 

    public String getAddBeforeSourceName() { 
     return addBeforeSourceName; 
    } 

    public void setAddBeforeSourceName(String addBeforeSourceName) { 
     this.addBeforeSourceName = addBeforeSourceName; 
    } 

    public void setApplicationContext(ApplicationContext applicationContext) { 
     this.applicationContext = applicationContext; 
    } 

    /** 
    * Subclasses can override this method to perform adjustments on the 
    * properties after they are read. 
    * <p> 
    * This should be done by getting, adding, removing, and updating properties 
    * as needed. 
    * 
    * @param props 
    *   properties to adjust 
    */ 
    protected void convertProperties(Properties props) { 
     // Override in subclass to perform conversions. 
    } 

    /** 
    * Creates a property source from the specified locations. 
    * 
    * @return PropertiesPropertySource instance containing the read properties 
    * @throws IOException 
    *    if properties cannot be read 
    */ 
    protected PropertySource<?> createPropertySource() throws IOException { 
     if (propertySourceName == null) { 
      throw new IllegalStateException("No property source name specified"); 
     } 

     // Load the properties file (or files) from specified locations. 
     final Properties props = new Properties(); 
     loadProperties(props); 

     // Convert properties as required. 
     convertProperties(props); 

     // Convert to property source. 
     final PropertiesPropertySource source = new PropertiesPropertySource(
       propertySourceName, props); 

     return source; 
    } 

    @Override 
    public void postProcessBeanFactory(
      ConfigurableListableBeanFactory beanFactory) throws BeansException { 
     try { 
      // Create the property source, and get its desired position in 
      // the list of sources. 
      if (logger.isDebugEnabled()) { 
       logger.debug("Creating property source [" + propertySourceName 
         + "]"); 
      } 
      final PropertySource<?> source = createPropertySource(); 

      // Register the property source. 
      final MutablePropertySources sources = getPropertySources(); 
      if (addBeforeSourceName != null) { 
       if (sources.contains(addBeforeSourceName)) { 
        if (logger.isDebugEnabled()) { 
         logger.debug("Adding property source [" 
           + propertySourceName + "] before [" 
           + addBeforeSourceName + "]"); 
        } 
        sources.addBefore(addBeforeSourceName, source); 
       } else { 
        logger.warn("Property source [" + propertySourceName 
          + "] cannot be added before non-existent source [" 
          + addBeforeSourceName + "] - adding at the end"); 
        sources.addLast(source); 
       } 
      } else { 
       if (logger.isDebugEnabled()) { 
        logger.debug("Adding property source [" 
          + propertySourceName + "] at the end"); 
       } 
       sources.addLast(source); 
      } 
     } catch (Exception e) { 
      throw new BeanInitializationException(
        "Failed to register property source", e); 
     } 
    } 

} 

は、このプロパティのソースファクトリクラスのデフォルトの順序はPropertySourcesPlaceholderConfigurerのデフォルトの順序よりも高い優先順位であるということです。

また、プロパティソースの登録はpostProcessBeanFactoryで行われます。つまり、PropertySourcesPlaceholderConfigurerを基準として正しい順序で実行されます。私は、InitializingBeanとafterPropertiesSetがorderパラメータを尊重しないという難しい方法を発見しました。私はこのアプローチを間違ったやり方として諦めました。

最後に、これはBeanFactoryPostProcessorであるため、依存関係の中で多くの配線を試みることは悪い考えです。したがって、クラスはApplicationContextAwareを使用して取得するアプリケーションコンテキストを通じて環境に直接アクセスします。私の場合は

は、私は以下のサブクラスを使用して実装され、パスワードのプロパティを解読するために、プロパティ源を必要:

package example; 

import java.util.Properties; 

/** 
* This is a property source factory that creates a property source that can 
* process properties for substituting into a Spring configuration. 
* <p> 
* The only thing that distinguishes this from a normal Spring property source 
* is that it decrypts encrypted passwords. 
* 
* @author rjsmith2 
* 
*/ 
public class PasswordPropertySourceFactory extends 
     AbstractPropertySourceFactory { 

    private static final PasswordHelper passwordHelper = new PasswordHelper(); 

    private String[] passwordProperties; 

    public String[] getPasswordProperties() { 
     return passwordProperties; 
    } 

    public void setPasswordProperties(String[] passwordProperties) { 
     this.passwordProperties = passwordProperties; 
    } 

    public void setPasswordProperty(String passwordProperty) { 
     this.passwordProperties = new String[] { passwordProperty }; 
    } 

    @Override 
    protected void convertProperties(Properties props) { 
     // Adjust password fields by decrypting them. 
     if (passwordProperties != null) { 
      for (String propName : passwordProperties) { 
       final String propValue = props.getProperty(propName); 
       if (propValue != null) { 
        final String plaintext = passwordHelper 
          .decryptString(propValue); 
        props.setProperty(propName, plaintext); 
       } 
      } 
     } 
    } 

} 

は最後に、私は私のSpring構成でプロパティソースファクトリをspecifed:

<!-- Enable property resolution via PropertySourcesPlaceholderConfigurer. 
    The order has to be larger than the ones used by custom property sources 
    so that those property sources are registered before any placeholders 
    are substituted. --> 
<context:property-placeholder order="1000" ignore-unresolvable="true" /> 

<!-- Register a custom property source that reads DB properties, and 
    decrypts the database password. --> 
<bean class="example.PasswordPropertySourceFactory"> 
    <property name="propertySourceName" value="DBPropertySource" /> 
    <property name="location" value="classpath:db.properties" /> 
    <property name="passwordProperty" value="db.password" /> 
    <property name="ignoreResourceNotFound" value="true" /> 

    <!-- Order must be lower than on property-placeholder element. --> 
    <property name="order" value="100" /> 
</bean> 

PropertySourcesPlaceholderConfigurerとAbstractPropertySourceFactoryの注文のデフォルトでは、Springの設定で順序を指定する必要はないでしょう。

これにもかかわらず、これは動作し、ではなく、はアプリケーションコンテキストの初期化で何か手を加える必要がありません。

1

私の場合、私はSpringをスタンドアロンアプリケーションで使用しています。デフォルトの設定をロードした後、別のプロパティファイル(遅延ロード設定)をconfigディレクトリに適用する必要があります。私の解決策はこのSpring Boot documentationに触発されたが、依存関係はSpring Bootではなかった。下記のソースコードを参照してください。

@PropertySources(@PropertySource(value = "classpath:myapp-default.properties")) 
public class PersistenceConfiguration { 

    private final Logger log = LoggerFactory.getLogger(getClass()); 

    private ConfigurableEnvironment env; 

    @Bean 
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurerDev(ConfigurableEnvironment env) { 
     return new PropertySourcesPlaceholderConfigurer(); 
    } 


    @Autowired 
    public void setConfigurableEnvironment(ConfigurableEnvironment env) { 
     for(String profile: env.getActiveProfiles()) { 
      final String fileName = "myapp-" + profile + ".properties"; 
      final Resource resource = new ClassPathResource(fileName); 
      if (resource.exists()) { 
       try { 
        MutablePropertySources sources = env.getPropertySources(); 
        sources.addFirst(new PropertiesPropertySource(fileName,PropertiesLoaderUtils.loadProperties(resource))); 
       } catch (Exception ex) { 
        log.error(ex.getMessage(), ex); 
        throw new RuntimeException(ex.getMessage(), ex); 
       } 
      } 
     } 
     this.env = env; 
    } 

    ... 

}