簡體   English   中英

如何使用 Spring @Value 從 java 屬性文件中填充 HashMap

[英]How to fill HashMap from java property file with Spring @Value

是否可以使用 Spring @Value,從屬性文件到 map 值到 HashMap。

目前我有這樣的東西,映射一個值不是問題。 但我需要在 HashMap 到期時使用 map 自定義值。 這樣的事情可能嗎?

@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

是否可以像這樣設置 map 鍵:值集

  • 名稱 1 = 100

  • 名稱 2 = 20

您可以使用類似於 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'}}")

您可以在官方文檔中了解更多信息

是否可以使用 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實用程序。 有關更多詳細信息,請參閱其文檔

然后,在你的一些豆子中:

@Component
public class MyBean {

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

}

最后,屬性:

service.expiration = name1:100,name2:20

這並不完全是您所問的,因為這個PropertySplitter使用一個轉換Map單個屬性,但我認為您可以切換到這種指定屬性的方式,或者修改PropertySplitter代碼,使其與更多層次結構相匹配你想要的方式。

從 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 錯誤。

我能想到的最快的基於 Spring Boot的解決方案如下。 在我的特定示例中,我將數據從一個系統遷移到另一個系統。 這就是為什么我需要一個名為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")

並為優先級中綴創建一個吸氣劑。 后者在我的情況下被證明是強制性的(盡管Sring Doc說擁有一個屬性優先級並用可變值初始化它就足夠了)

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
    }
}

我根據上一篇文章提出了一個解決方案。

在 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;
    }
}

當我想將所有以特定值開頭的屬性映射到 HashMap 時,使用 @Value 注釋:

@Service
public class MyServiceImpl implements MyService {

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

使用@Valueapplication.yml屬性中提取Map 的解決方案編碼為多行

應用程序.yml

other-prop: just for demo 

my-map-property-name: "{\
         key1: \"ANY String Value here\", \  
         key2: \"any number of items\" , \ 
         key3: \"Note the Last item does not have comma\" \
         }"

other-prop2: just for demo 2 

這里,我們的地圖屬性“my-map-property-name”的值以JSON格式存儲在一個字符串中,我們在行尾使用\\ 實現了多行

我的JavaClass.java

import org.springframework.beans.factory.annotation.Value;

public class myJavaClass {

@Value("#{${my-map-property-name}}") 
private Map<String,String> myMap;

public void someRandomMethod (){
    if(myMap.containsKey("key1")) {
            //todo...
    } }

}

更多解釋

  • \\在 yaml 中用於將字符串分成多行

  • \\"是 yaml 字符串中 "(quote) 的轉義字符

  • yaml 中的{key:value} JSON 將被 @Value 轉換為 Map

  • #{ }是SpEL 表達式,可以在@Value 中使用,轉換json int Map 或Array / list 參考

在 Spring Boot 項目中測試

使用與 Yaml 名稱相同的變量名稱

例如:

private final HashMap<String, String> expiration 

代替

private final HashMap<String, String> expirations 

或者屬性文件中類似的東西

org.code=0009,0008,0010
org.code.0009.channel=30,40
org.code.0008.channel=30,40
org.code.0010.channel=30,40

在 Java 中,讀取 org.code 然后遍歷每個 org.code 並構建 org.code..channel 並將其放入 map....

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM