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

如何在Spring Boot中注册新集合工厂以支持Guava ImmutableList

解决Spring Boot @ConfigurationProperties注入Guava ImmutableList的问题

问题核心

要在配置属性类中直接注入ImmutableList,无需手动调用ImmutableList.copyOf(...),但Spring Boot的属性绑定机制会通过CollectionFactory.createCollection(...)尝试直接创建集合实例,而ImmutableList没有公开可实例化的构造器,导致绑定失败;同时常规的Converter无法被触发,因为Spring优先尝试创建目标类型集合。

解决方案

方案一:注册@ConfigurationPropertiesBinding注解的GenericConverter

创建支持泛型的转换器,让Spring配置属性绑定机制识别并使用它完成Collection到ImmutableList的转换:

Java实现

import com.google.common.collect.ImmutableList;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Set;

@ConfigurationPropertiesBinding
@Component
public class CollectionToImmutableListConverter implements GenericConverter {

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Set.of(new ConvertiblePair(Collection.class, ImmutableList.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {
            return ImmutableList.of();
        }
        return ImmutableList.copyOf((Collection<?>) source);
    }
}

Kotlin实现

import com.google.common.collect.ImmutableList
import org.springframework.core.convert.TypeDescriptor
import org.springframework.core.convert.converter.GenericConverter
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding
import org.springframework.stereotype.Component

@ConfigurationPropertiesBinding
@Component
class CollectionToImmutableListConverter : GenericConverter {

    override fun getConvertibleTypes(): Set<GenericConverter.ConvertiblePair> {
        return setOf(GenericConverter.ConvertiblePair(Collection::class.java, ImmutableList::class.java))
    }

    override fun convert(source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor): Any? {
        return source?.let { ImmutableList.copyOf(it as Collection<*>) } ?: ImmutableList.of()
    }
}

方案二:自定义BinderCustomizer扩展绑定逻辑

如果方案一未生效,直接修改Binder的配置,强制添加转换器:

import com.google.common.collect.ImmutableList;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.BinderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.util.Collection;

@Configuration
public class ImmutableListBindingConfig {

    @Bean
    public BinderCustomizer immutableListBinderCustomizer() {
        return binder -> binder.withConverter((Converter<Collection<?>, ImmutableList<?>>) ImmutableList::copyOf);
    }
}

方案三:备选:手动转换(不推荐,仅作兜底)

如果上述方案都无法使用,可先绑定普通List,再通过@Bean转换为ImmutableList:

// 原始配置类,绑定普通List
@ConfigurationProperties(prefix = "app")
data class AppRawProperties(val items: List<String>)

// 转换为ImmutableList的配置类
@Configuration
class AppConfig {
    @Bean
    fun appProperties(rawProperties: AppRawProperties) = 
        AppProperties(ImmutableList.copyOf(rawProperties.items))
}

data class AppProperties(val immutableItems: ImmutableList<String>)

关键说明

  • 必须确保Guava依赖已正确引入项目;
  • 使用@ConfigurationPropertiesBinding注解是核心:它限定转换器仅作用于配置属性绑定流程,不会干扰Spring其他转换逻辑;
  • 选择GenericConverter而非普通Converter,是因为它能处理泛型类型匹配,适配ImmutableList<T>的任意泛型参数场景;
  • 该方案适配Kotlin/Java混合环境:ImmutableList从根源上避免Java代码通过强制转换修改集合的问题,比Kotlin只读List更安全。

内容的提问来源于stack exchange,提问作者Cobra1117

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 11:03:14