暴露不可变的Set<String>配置

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

我需要在我的应用程序中以集合的形式公开一些配置,但是我需要它们不可修改并且我正在寻找实现此目的的最佳方法。

我的配置

我需要公开此配置以在业务逻辑操作中执行,如

mySet.set1().constains("Value")

这是application.yml

中的配置
app:
  my-set:
    set-1: 'Value1,Value2'
    set-2: 'Value3,Value4'

问题

如果我尝试在记录或这样编写的类中公开它:

@ConfigurationProperties("app.my-set")
public record MySet(Set<String> set1, Set<String> set2) {
}

这 2 个集合是最终的,所以我无法重新分配它们,但我仍然可以修改其中的元素,就像这样:

@SpringBootTest
@Slf4j
 class MySetTest {
    @Autowired
    private MySet mySet;

    @Test
    void testMutableSet(){
        log.info("Initial set: [{}]", mySet);
        assertTrue(mySet.set1().contains("Value1") && mySet.set1().contains("Value2"));
        assertTrue(mySet.set2().contains("Value3") && mySet.set2().contains("Value4"));
        mySet.set1().remove("Value1");
        mySet.set2().remove("Value3");
        mySet.set1().add("Custom1");
        mySet.set2().add("Custom2");
        log.info("Final set: [{}]", mySet);
        assertFalse(mySet.set1().contains("Value1"));
        assertFalse(mySet.set2().contains("Value3"));
        assertTrue(mySet.set1().contains("Custom1") && mySet.set1().contains("Value2"));
        assertTrue(mySet.set2().contains("Custom2") && mySet.set2().contains("Value4"));
    }

}

我的解决方案

为了隐藏在类范围之外修改它们的可能性,我使用了一种解决方法: 我实现了返回unmodifyingSet的方法:

@ConfigurationProperties("app.my-set")
public record MySet(Set<String> set1, Set<String> set2) {
    
    public Set<String> set1() {
        return Collections.unmodifiableSet(set1);
    }

    public Set<String> set2() {
        return Collections.unmodifiableSet(set2);
    }
}

通过这种解决方法,当我需要将其注入到其他类中时,我只能看到 MyImmutableSet 接口,而无法修改 Sets 内的值。

@SpringBootTest
@Slf4j
 class MyImmutableSetTest {
    @Autowired
    private MySet mySet;

    @Test
    void testImmutable(){
        log.info("Initial set: [{}]", mySet);
        assertTrue(mySet.set1().contains("Value1") && mySet.set1().contains("Value2"));
        assertTrue(mySet.set2().contains("Value3") && mySet.set2().contains("Value4"));
        assertThrows(UnsupportedOperationException.class, () -> mySet.set1().remove("Value1"));
        assertThrows(UnsupportedOperationException.class, () -> mySet.set2().remove("Value3"));
        assertThrows(UnsupportedOperationException.class, () -> mySet.set1().add("Custom1"));
        assertThrows(UnsupportedOperationException.class, () -> mySet.set2().add("Custom2"));
        log.info("Final set: [{}]", mySet);
        assertFalse(mySet.set1().contains("Custom1"));
        assertFalse(mySet.set2().contains("Custom2"));
        assertTrue(mySet.set1().contains("Value1") && mySet.set1().contains("Value2"));
        assertTrue(mySet.set2().contains("Value3") && mySet.set2().contains("Value4"));
    }

}

我需要什么

我真的不喜欢这种解决方法,所以我正在寻找一种更干净的解决方案来提高可维护性,也许还需要一些注释。 我已经尝试过使用仅公开两组的界面并从记录中删除

public
访问修饰符,但它看起来比唯一的记录更糟糕。
有没有人有其他解决方案可以分享?
预先感谢您的帮助!

java spring spring-boot configuration record
1个回答
0
投票

我自己所做的是使用记录构造函数简写来替换输入:

@ConfigurationProperties("app.my-set")
public record MySet(Set<String> set1, Set<String> set2) {

    public MySet {
        set1 = Set.copyOf(set1);
        set2 = Set.copyOf(set2);
    }
}

如果需要,你可以制作这些

null-safe
set1 = set1 == null ? Set.of() : Set.copyOf(set1);

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