15

私は、" Config" というユーティリティ クラスを持つアプリケーション ライブラリに取り組んでいます。このユーティリティ クラスは、Spring オブジェクトに支えられ、Environmentすべてのアプリケーション構成値に対して厳密に型指定されたゲッターを提供します。

構成のプロパティ ソースは、環境 (DEV/PROD) と使用法 (スタンドアロン/テスト/webapp) によって異なり、既定のもの (システムと環境の props) からカスタム データベースと JNDI ソースにまで及びます。

私が苦労しているのは、このライブラリを使用するアプリが で使用されるプロパティ ソースを簡単に構成できるようにする方法Environmentです。ConfigPropertySourcesPlaceholderConfigurer

私たちはまだ XML 構成を使用しているので、理想的にはこれを XML のようなもので構成できます。

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

...そして、Environment のプロパティ ソース コレクションに何らかの方法で注入します。

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

何か案は?

4

5 に答える 5

11

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

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

を。Java 構成の使用:

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

}

b. ここで説明されている方法で、カスタムApplicationContextInitializerを使用する

于 2013-01-19T17:18:07.547 に答える
10

私はうまくいくように思われる次のことを思いつきましたが、私はSpringにかなり慣れていないので、さまざまなユースケースでどのように機能するかわかりません.

基本的にはPropertySourcesPlaceholderConfigurer、セッターを拡張および追加して、ユーザーがPropertySourceXML でオブジェクトのリストを簡単に構成できるようにする方法です。作成後、プロパティ ソースは現在の にコピーされますEnvironment

これにより、基本的にプロパティ ソースを 1 か所で構成できますが、プレースホルダー構成と 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));
        }
    }
}

基本構成を示す beans.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>
于 2013-01-21T00:42:35.250 に答える
5

以下は、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";
    }

}

テスト Bean (getValue()が出力されます"IT WORKS"):

public class TestBean1 {

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

   public String getValue() {
      return value;
   }
}
于 2015-07-09T07:58:19.183 に答える
4

私の場合Spring、スタンドアロンアプリケーションで使用している場合、デフォルト構成をロードした後、構成ディレクトリにある別のプロパティファイル(遅延ロード構成)を適用する必要がある場合があります。Spring Boot私の解決策はこのドキュメントに触発されましたが、依存関係はありません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;
    }

    ...

}
于 2016-08-27T02:16:09.887 に答える
2

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

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

いくつかの調査と試行錯誤の結果、Spring 構成内からカスタム プロパティ ソースを登録できることわかりましたが、その方法には注意が必要です。コンテキストで PropertySourcesPlaceholderConfigurers を実行する前に、ソースを登録する必要があります。これを行うには、ソース登録を PriorityOrdered およびソースを使用する PropertySourcesPlaceholderConfigurer よりも優先順位の高い BeanFactoryPostProcessor にします。

私は仕事をするこのクラスを書きました:

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 が順序パラメーターを尊重しないという難しい方法を発見し、そのアプローチは間違っていて冗長であるとしてあきらめました。

最後に、これは 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 構成でプロパティ ソース ファクトリを指定しました。

<!-- 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 構成で順序を指定する必要さえない可能性があります。

それにもかかわらず、これは機能し、アプリケーション コンテキストの初期化をいじる必要はありません。

于 2016-02-18T19:20:44.900 に答える