SpringBoot:从 YAML 文件与 .properties 文件注入映射

问题描述 投票:0回答:2

实际上,我可以从定义如下的 YAML 文件加载/注入地图:

# custom-auth.yaml
auth:
  providers:
    keycloak:
      enabled: true
      realmUrl: http://localhost:38087/auth/realms/my-app-realm
      clientId: keycloak-client-id
      clientSecret: keycloak-client-secret
      logoutUrl: keycloak-logout-url
    trustbuilder:
      enabled: false
      clientId: tb-1-client-id
      clientSecret: tb-1-client-secret
      logoutUrl: tb-1-logout-url

配置类定义如下:

@Data
@Configuration
@ConfigurationProperties(prefix = "auth")
@PropertySource(value="classpath:custom-auth.yaml", factory = AuthYamlFactory.class)
public class OIDCConfiguration {
    private Map<String, OIDCProvider> providers;

    @Data
    public static class OIDCProvider {
        private boolean enabled;
        private String realmUrl;
        private String clientId;
        private String clientSecret;
        private String logoutUrl;
    }
}

我们需要一个工厂类来加载上面的自定义 YAML 文件:

public class AuthYamlFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) {
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(encodedResource.getResource());

        Properties properties = factory.getObject();

        return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
    }
}

测试证明有效:

@SpringBootTest
public class OIDCConfigurationTest {

    @Autowired
    private OIDCConfiguration oidcConfiguration;

    @Test
    public void shouldReadSeparateValues() {
        Map<String, OIDCConfiguration.OIDCProvider> providers = oidcConfiguration.getProviders();
        assertThat(providers).containsOnlyKeys("keycloak", "trustbuilder");

        assertThat(providers.get("keycloak").isEnabled()).isTrue();
        assertThat(providers.get("keycloak").getClientId()).isEqualTo("keycloak-client-id");
        System.out.println(providers.get("keycloak"));
    }
...
}

我想知道为什么我不能实现相同但使用

application.properties
文件定义如下:

auth.providers.keycloak.enabled=true
auth.providers.keycloak.clientSecret=keycloak-client-secret
auth.providers.keycloak.clientId=keycloak-client-id

auth.providers.trustbuilder.enabled=false
auth.providers.trustbuilder.clientSecret=trustbuilder-client-secret
auth.providers.trustbuilder.clientId=trustbuilder-client-id

配置类会有所不同,因为我无法将字符串映射到

OIDCProvider
实例:

@Data
@Configuration
@ConfigurationProperties("auth")
public class OIDCPropertiesConfiguration {
    private Map<String, String> providers;
...
}

测试班:

@SpringBootTest
public class OIDCConfigurationTest {

    @Autowired
    private OIDCConfiguration oidcConfiguration;

    @Autowired
    private OIDCPropertiesConfiguration oidcPropertiesConfiguration;
...
@Test
    public void shouldReadSeparateValuesFromPropertiesFile() {
        Map<String, String> providers = oidcPropertiesConfiguration.getProviders();
        assertThat(providers).containsOnlyKeys("keycloak", "trustbuilder");

        assertThat(providers.get("keycloak.enabled")).isEqualTo(true);
        assertThat(providers.get("keycloak.clientId")).isEqualTo("keycloak-client-id");
        System.out.println(providers.get("keycloak"));
    }
}

上面的测试失败了,因为我在这种提供者中有 6 个,而不是 2 个提供者地图:

{
keycloak.enabled=true, 
keycloak.clientSecret=keycloak-client-secret, 
keycloak.clientId=keycloak-client-id, 
trustbuilder.enabled=false, 
trustbuilder.clientSecret=trustbuilder-client-secret, 
trustbuilderclientId=trustbuilder-client-id
}

使用索引值更改

application.properties
文件中的数据,如下所示:

auth.providers[0].enabled=true
auth.providers[0].clientSecret=keycloak-client-secret
auth.providers[0].clientId=keycloak-client-id

auth.providers[1].enabled=true
auth.providers[1].clientSecret=keycloak-client-secret
auth.providers[1].clientId=keycloak-client-id

没有改变结果,地图中仍然有 6 个条目,而不是 2 个。

为什么会这样? 以及如何实现与 YAML 文件相同的效果?有什么线索吗?

spring-boot yaml properties-file
2个回答
0
投票

只是一个想法:

  1. 在您的属性文件中添加一个属性说“

    auth.providers
    ”和作为 一个值把一个 JSON 对象作为一个包含你的上下文的字符串 地图

  2. 在您的代码中将该值作为字符串读取

        @Value("${auth.providers}" private String authProvidersStr
    
  3. 使用任何 Json 库将 Json 对象解析为地图。 无论您喜欢什么,这都可以使用 Json Jackson 库或 Gson 库轻松完成。我编写了自己的 JsonUtils 辅助实用程序,它是 Json-Jackson 库的

    ObjectMapper
    类的精简包装器。使用此实用程序,您的代码将非常简单:


try {
    Map<String, Object> authProviders = JsonUtils.readObjectFromJsonString(authProvidersStr, Map.class);
catch(IOException ioe) {
  ...
}

JsonUtils
类是我编写和维护的开源 MgntUtils java 库的一部分。这里是JsonUtils Javadoc,库可以作为maven artifact或从github(包括源代码和Javadoc)获得


0
投票

最后,我想出了如何让它发挥作用。 我们开始:

  1. 定义
    application.properties
    文件中的值如下:
auth.providers.keycloak.name=dev.server.com
auth.providers.keycloak.url=https://dev.server.com
auth.providers.trustbuilder.name=prod.server.com
auth.providers.trustbuilder.url=https://prod.server.com
  1. 创建配置类如下:
@Configuration
@ConfigurationProperties("auth")
public class AuthConfig {
// The annotation processor automatically considers inner classes as nested properties.

    private Map<String, AuthProvider> providers = new HashMap<>();

    public Map<String, AuthProvider> getProviders() {
        return providers;
    }

    public void setProviders(Map<String, AuthProvider> providers) {
        this.providers = providers;
    }

    public static class  AuthProvider {
        String name;
        String url;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }
}
  1. 让我们在测试课上测试一下:
@SpringBootTest
class AuthConfigTest {

    @Autowired
    private AuthConfig authConfig;

    @Test
    public void authProvidersShouldContainRightValues() {
        Map<String, AuthConfig.AuthProvider> providersData = authConfig.getProviders();
        Set<String> providers = providersData.keySet();
        assertThat(providers).containsExactly("keycloak", "trustbuilder");
    }

    @Test
    public void providerShouldHaveAttributes() {
        Map<String, AuthConfig.AuthProvider> providersData = authConfig.getProviders();
        assertEquals(providersData.get("keycloak").getName(), "dev.server.com");
        assertEquals(providersData.get("keycloak").getUrl(), "https://dev.server.com");
    }
}

当然,我们可以在 Spring Boot 应用程序中的任何位置访问配置值。这是它在虚拟控制器中的使用示例:

@RestController
public class HelloController {

    private final AuthConfig authConfig;

    public HelloController(AuthConfig authConfig) {
        this.authConfig = authConfig;
    }

    @GetMapping("/greeting")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
        System.out.println("++++++++++++++++++");
        Map<String, AuthConfig.AuthProvider> servers = authConfig.getProviders();
        Set<Map.Entry<String, AuthConfig.AuthProvider>> entries = servers.entrySet();
        for (Map.Entry<String, AuthConfig.AuthProvider> entry : entries) {
            System.out.println(entry.getKey());
            System.out.println( entry.getValue());
        }
        return String.format("Hello %s!", name);
    }
}

Et voilà。希望这可以帮助。 瞧。

© www.soinside.com 2019 - 2024. All rights reserved.