You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 01:30:18