Spring Boot中如何暴露不可变的Set<String>配置?
如何实现不可变的Set类型配置暴露?
我需要在Spring应用中以Set形式暴露部分配置,且要求这些配置完全不可变,正在寻找最优实现方式。
我的配置
配置定义在application.yml中,需要暴露给业务逻辑使用(例如mySet.set1().contains("Value")):
app: my-set: set-1: 'Value1,Value2' set-2: 'Value3,Value4'
现有问题
如果直接用@ConfigurationProperties结合Record暴露配置:
@ConfigurationProperties("app.my-set") public record MySet(Set<String> set1, Set<String> set2) { }
虽然两个Set字段是final的无法重新赋值,但外部仍可以修改集合内的元素。测试代码验证了这一点:
@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")); } }
临时解决方案
为了避免外部修改集合元素,我临时重写了Record的访问方法,返回不可变集合:
@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); } }
这个方案能阻止外部修改集合元素,测试验证有效:
@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")); } }
需求
我并不满意这个临时方案,希望找到更简洁、可维护性更强的实现方式,比如通过注解实现。我曾尝试用接口暴露两个Set并移除Record的public修饰符,但效果更差。请问有没有其他可行的方案?
内容的提问来源于stack exchange,提问作者Alexxxx
相关产品推荐
相关产品推荐

