web-dev-qa-db-ja.com

HashMapをJavaプロパティファイルからSpring @Valueで埋める方法

Spring @Valueを使用して、プロパティファイルの値をHashMapにマップすることは可能ですか?.

現在、私はこのようなものを持っています、そして1つの値をマッピングすることは問題ではありません。ただし、HashMapの有効期限でカスタム値をマップする必要があります。このようなことは可能ですか?

@Service
@PropertySource(value = "classpath:my_service.properties")
public class SomeServiceImpl implements SomeService {


    @Value("#{conf['service.cache']}")
    private final boolean useCache = false;

    @Value("#{conf['service.expiration.[<custom name>]']}")
    private final HashMap<String, String> expirations = new HashMap<String, String>();

プロパティファイル: 'my_service.properties'

service.cache=true
service.expiration.name1=100
service.expiration.name2=20

このキーのようにマッピングすることは可能ですか?値セット

  • name1 = 100

  • name2 = 20

34
d-sauer

以前の投稿に触発されたソリューションを1つ作成します。

Spring構成にプロパティファイルを登録します。

<util:properties id="myProp" location="classpath:my.properties"/>

そして、コンポーネントを作成します:

@Component("PropertyMapper")
public class PropertyMapper {

    @Autowired
    ApplicationContext applicationContext;

    public HashMap<String, Object> startWith(String qualifier, String startWith) {
        return startWith(qualifier, startWith, false);
    }

    public HashMap<String, Object> startWith(String qualifier, String startWith, boolean removeStartWith) {
        HashMap<String, Object> result = new HashMap<String, Object>();

        Object obj = applicationContext.getBean(qualifier);
        if (obj instanceof Properties) {
            Properties mobileProperties = (Properties)obj;

            if (mobileProperties != null) {
                for (Entry<Object, Object> e : mobileProperties.entrySet()) {
                    Object oKey = e.getKey();
                    if (oKey instanceof String) {
                        String key = (String)oKey;
                        if (((String) oKey).startsWith(startWith)) {
                            if (removeStartWith) 
                                key = key.substring(startWith.length());
                            result.put(key, e.getValue());
                        }
                    }
                }
            }
        }

        return result;
    }
}

そして、@ Valueアノテーションを使用して、specifix値で始まるすべてのプロパティをHashMapにマップしたい場合:

@Service
public class MyServiceImpl implements MyService {

    @Value("#{PropertyMapper.startWith('myProp', 'service.expiration.', true)}")
    private HashMap<String, Object> portalExpirations;
15
d-sauer

Spring @Valueを使用して、プロパティファイルの値をHashMapにマップすることはできますか?

はい、そうです。コードと Spel の少しの助けを借りて。

まず、このシングルトンSpring-beanを検討します(スキャンする必要があります)。

@Component("PropertySplitter")
public class PropertySplitter {

    /**
     * Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
     */
    public Map<String, String> map(String property) {
        return this.map(property, ",");
    }

    /**
     * Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
     */
    public Map<String, List<String>> mapOfList(String property) {
        Map<String, String> map = this.map(property, ";");

        Map<String, List<String>> mapOfList = new HashMap<>();
        for (Entry<String, String> entry : map.entrySet()) {
            mapOfList.put(entry.getKey(), this.list(entry.getValue()));
        }

        return mapOfList;
    }

    /**
     * Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
     */
    public List<String> list(String property) {
        return this.list(property, ",");
    }

    /**
     * Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
     */
    public List<List<String>> groupedList(String property) {
        List<String> unGroupedList = this.list(property, ";");

        List<List<String>> groupedList = new ArrayList<>();
        for (String group : unGroupedList) {
            groupedList.add(this.list(group));
        }

        return groupedList;

    }

    private List<String> list(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
    }

    private Map<String, String> map(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
    }

}

注:PropertySplitterクラスはGuavaのSplitterユーティリティを使用します。詳細については、 そのドキュメント を参照してください。

次に、いくつかのBeanで:

@Component
public class MyBean {

    @Value("#{PropertySplitter.map('${service.expiration}')}")
    Map<String, String> propertyAsMap;

}

そして最後に、プロパティ:

service.expiration = name1:100,name2:20

このPropertySplitterは、transformedからMapに変換される単一のプロパティで機能するため、あなたが尋ねたとおりのものではありませんが、プロパティを指定するこの方法に切り替えるか、PropertySplitterコードを変更して、より階層的な方法に一致させることができます。

SPEL jsonのような構文を使用して、プロパティファイルに単純なマップまたはリストのマップを記述できます。

simple.map={'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}

map.of.list={\
  'KEY1': {'value1','value2'}, \
  'KEY2': {'value3','value4'}, \
  'KEY3': {'value5'} \
 }

複数行のプロパティに\を使用して読みやすくしました

次に、Javaで、次のように@Valueを使用して自動的にアクセスして解析できます。

@Value("#{${simple.map}}")
Map<String, String> simpleMap;

@Value("#{${map.of.list}}")
Map<String, List<String>> mapOfList;

ここで${simple.map}を使用すると、@Valueはプロパティファイルから次の文字列を取得します。

"{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}"

次に、インライン化されたかのように評価されます

@Value("#{{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}}")

詳しくは 公式ドキュメント をご覧ください。

15
Marc Bouvier

Spring 4.1.x(特定のバージョンは覚えていませんが)から、次のようなことができます

@Value("#{${your.properties.key.name}}")
private Map<String, String> myMap;

プロパティファイルのyour.properties.key.nameは次のようになります

your.properties.key.name={\
    name1 : 100, \
    name2 : 200 \
}

PropertySourcesPlaceholderConfigurer Beanを作成してアプリで動作するようにし、コードをテストするユニットテストコードを記述している場合は、プロパティ値の$ {...}プレースホルダーが期待どおりに動作しないことを確認してください。奇妙なSpringELエラーが表示されます。

10
wonhee

私が考えることができる最も速いSpringBootベースのソリューション私の特定の例では、あるシステムから別のシステムにデータを移行しています。そのため、priorityというフィールドのマッピングが必要です。

まず、次のようなプロパティファイル(priority-migration.properties)を作成しました。

my.prefix.priority.0:0
my.prefix.priority.10:1
my.prefix.priority.15:2
my.prefix.priority.20:2
another.prefix.foo:bar

クラスパスに配置します。

Spring管理Bean /コンポーネントでマップを使用する場合、クラスに次の注釈を付けます。

@Component
@PropertySource("classpath:/priority-migration.properties")

マップで実際に必要なのは、もちろん、my.prefixのプレフィックスが付いたキー/値のペアのみです。つまり、この部分です。

{
    0:0
    10:1
    15:2
    20:2
}

それを実現するには、コンポーネントに注釈を付ける必要があります

@ConfigurationProperties("my.prefix")

priority中置用のゲッターを作成します。私の場合、後者は必須であることが判明しました( Sring Doc は、プロパティpriorityを持ち、初期化するのに十分であると言いますが可変値を持つ)

private final Map<Integer, Integer> priorityMap = new HashMap<>();

public Map<Integer, Integer> getPriority() {
    return priorityMap;
}

最後に

次のようになります。

@Component
@ConfigurationProperties("my.prefix")
@PropertySource("classpath:/priority-migration.properties")
class PriorityProcessor {

    private final Map<Integer, Integer> priorityMap = new HashMap<>();

    public Map<Integer, Integer> getPriority() {
        return priorityMap;
    }

    public void process() {

        Integer myPriority = priorityMap.get(10)
        // use it here
    }
}
7