使用ConfigurationProperties以通用方式填充Map

Han*_*ier 40 java spring spring-boot

我想知道,如果有一个通用的方法用属性填充地图你只知道前缀.

假设有很多属性,比如

namespace.prop1=value1
namespace.prop2=value2
namespace.iDontKnowThisNameAtCompileTime=anothervalue
Run Code Online (Sandbox Code Playgroud)

我想有一个通用的方法在地图中填充这个属性,比如

@Component
@ConfigurationProperties("namespace")
public class MyGenericProps {
    private Map<String, String> propmap = new HashMap<String, String>();

    // setter and getter for propmap omitted

    public Set<String> returnAllKeys() {
        return propmap.keySet();
    }
}
Run Code Online (Sandbox Code Playgroud)

或者是否有另一种方便的方法来收集具有特定前缀的所有属性,而不是迭代环境中的所有PropertySources?

谢谢Hansjoerg

And*_*son 70

只要你乐意将每个属性添加到地图中,而不仅仅是那些你事先不知道的属性,你可以这样做@ConfigurationProperties.如果你想获取下面的所有内容,namespace那么你需要使用一个空前缀并为名为的地图提供一个getter namespace:

@ConfigurationProperties("")
public class CustomProperties {

    private final Map<String, String> namespace = new HashMap<>();

    public Map<String, String> getNamespace() {
        return namespace;
    }

}
Run Code Online (Sandbox Code Playgroud)

Spring Boot使用该getNamespace方法检索地图,以便可以向其添加属性.有了这些属性:

namespace.a=alpha
namespace.b=bravo
namespace.c=charlie
Run Code Online (Sandbox Code Playgroud)

namespace地图将包含三个条目:

{a=alpha, b=bravo, c=charlie}
Run Code Online (Sandbox Code Playgroud)

如果属性嵌套得更深,例如:

namespace.foo.bar.a=alpha
namespace.foo.bar.b=bravo
namespace.foo.bar.c=charlie
Run Code Online (Sandbox Code Playgroud)

然后你会使用namespace.foo作为前缀,并重新命名namespace,并getNamespaceCustomPropertiesbargetBar分别.

请注意,您应该应用于@EnableConfigurationProperties您的配置以启用支持@ConfigurationProperties.然后,您可以使用该批注引用要处理的任何bean,而不是@Bean为它们提供方法,或者使用@Component组件扫描发现它们:

@SpringBootApplication
@EnableConfigurationProperties(CustomProperties.class)
public class YourApplication {
    // …
}
Run Code Online (Sandbox Code Playgroud)

  • 你能解释一下为什么我们不应该用 @Component 注释 @ConfigurationProperties 吗?这是在引导文档中完成的。您是说首选使用 @EnableConfiguratinoProperties 吗? (2认同)
  • 非常感谢,我没有意识到 getter 方法名称很重要,我认为只有成员变量名称很重要。但这是有道理的,因为它正在获取 Map 对象的一个​​实例,然后添加到它。 (2认同)

MMa*_*rin 12

除此之外,我的问题是我没有多个简单的键/值属性,而是整个对象:

zuul:
  routes:
    query1:
      path: /api/apps/test1/query/**
      stripPrefix: false
      url: "https://test.url.com/query1"
    query2:
       path: /api/apps/test2/query/**
       stripPrefix: false
       url: "https://test.url.com/query2"
    index1:
       path: /api/apps/*/index/**
       stripPrefix: false
       url: "https://test.url.com/index"
Run Code Online (Sandbox Code Playgroud)

按照 Jake 的建议,我尝试将 Map 与这样的 Pojo 一起使用:

@ConfigurationProperties("zuul")
public class RouteConfig {
    private Map<String, Route> routes = new HashMap<>();

    public Map<String, Route> getRoutes() {
        return routes;
    }

    public static class Route {
        private String path;
        private boolean stripPrefix;
        String url;

        // [getters + setters]
    }
}
Run Code Online (Sandbox Code Playgroud)

像魅力一样工作,谢谢!

  • 注意:为您想要从属性中填充的字段提供 getter 和 setter 是绝对重要的。即使这些字段是公共的,如果它们没有setter(或者我想在地图的情况下是getter),Spring也不会填充它们。我仍然不明白的一件事是 Spring 如何能够推断出映射值应该具有什么类(在本例中为“Route”),因为我一直明白通用类型信息在运行时不可用,但不知何故它作品。我猜是魔法。 (2认同)

ash*_*bar 5

我疯狂地试图理解为什么@Andy 的答案对我不起作用(因为Map它仍然是空的)只是为了意识到我有 Lombok 的@Builder注释妨碍了,它添加了一个非空的构造函数。我添加这个答案是为了强调为了@ConfigurationProperties工作Map,值类型必须有一个 No-Arguments 构造函数。Spring的文档中也提到了这一点:

这种安排依赖于默认的空构造函数,而 getter 和 setter 通常是强制性的......

我希望这会为其他人节省一些时间。