SpringBoot:从YAML文件注入Mapvs .属性文件

wecizke3  于 2023-04-30  发布在  Spring
关注(0)|答案(2)|浏览(241)

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

# 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

配置类将有所不同,因为我无法将StringMap到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个这样的提供者Map,而不是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

没有改变结果,Map中仍然有6个条目,而不是2个。
为什么会这样呢?如何实现与YAML文件相同的功能?有线索吗?

rn0zuynd

rn0zuynd1#

只是一个想法:
1.在你的属性文件中添加一个单一的属性,比如“auth.providers”,并将一个JSON对象作为一个包含Map上下文的字符串作为一个值。
1.在代码中将该值读取为String

@Value("${auth.providers}" private String authProvidersStr

1.使用任何Json库将Json对象解析为map。这可以很容易地使用JsonJackson库或Gson库来完成,无论您喜欢什么。我写了自己的JsonUtils辅助工具,它是Json-Jackson库的ObjectMapper类的一个薄 Package 器。有了这个实用程序,你的代码会非常简单:

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

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

k3bvogb1

k3bvogb12#

最后,我想出了如何让它工作。开始了
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

# you can also use an array-based declaration like this:
auth.providers[keycloak][name]=dev.server.com
...# other properties declared the same way, but I find it less readable and more error-prone.

1.创建一个配置类,如下所示:

@Configuration
@ConfigurationProperties("auth")
public class AuthConfig {
// The annotation processor automatically considers inner classes as nested properties.
// Setters/getters are mandatory, otherwise it will raise a "property setter not found" error.

    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");
    }
}

当然,我们可以在Sping 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);
    }
}

好了希望能帮上忙瞧

相关问题