我希望能够在我的Spring Boot@ConfigurationProperties
类中注入ImmutableList
个实例(来自Guava)(而不必 for each 值执行ImmutableList.copyOf(...)
操作).
我try 添加了各种转换器,但看起来属性绑定试图直接构建集合,所以像public class ListToImmutableListConverter implements Converter<List<Any>, ImmutableList<Any>>
这样的转换器永远不会被调用.
根据堆栈跟踪,在CollectionFactory.createCollection(...)
中创建属性似乎失败,但我没有看到任何用于在其中添加新集合类型的扩展点.有没有一种简单的方法来向Spring注册新的集合类型?
编辑:作为参考,这是一个Kotlin/Java混合环境,所以我们不能仅仅依靠Kotlin的"MuableList vs List",因为Java Bean可以获得List
并在其上调用.add(...)
.
代码:
fun main(args: Array<String>) {
val context = SpringApplication.run(Microservice::class.java, *args)
println("--------------------------------------------------------------------------------------------------------")
context.getBean(SomeBean::class.java).doStuff()
println("--------------------------------------------------------------------------------------------------------")
SpringApplication.exit(context)
}
@SpringBootApplication
@ConfigurationPropertiesScan
class Microservice
@ConfigurationProperties(prefix = "test")
@Validated
class Properties(
val someList: ImmutableList<String>
)
@Component
@ConfigurationPropertiesBinding
class ListToImmutableListConverter : Converter<List<Any>, ImmutableList<Any>> {
override fun convert(source: List<Any>): ImmutableList<Any> = source.toImmutableList()
}
@Component
class SomeBean(
private val properties: Properties
) {
fun doStuff() {
println(properties.someList)
}
}
示例application.yaml
文件:
spring.application.name: test-service
test:
some-list:
- foo
- bar
- baz
try 了其他转换方法(使用TODO
,只是为了看看Spring是否会调用转换器):
@Component
@ConfigurationPropertiesBinding
class ListToImmutableListConverter : GenericConverter {
override fun getConvertibleTypes(): Set<GenericConverter.ConvertiblePair> = immutableSetOf(
GenericConverter.ConvertiblePair(List::class.java, ImmutableList::class.java)
)
override fun convert(source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor): Any? {
TODO("Not yet implemented")
}
}
@Component
@ConfigurationPropertiesBinding
class ListToImmutableListConverter : ConverterFactory<List<*>, ImmutableList<*>> {
override fun <T : ImmutableList<*>?> getConverter(targetType: Class<T>): Converter<List<*>, T> {
TODO("Not yet implemented")
}
}
故障日志(log)示例:
18:56:05.069 [main] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someBean' defined in file [****]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'test-test.spring.Properties': Could not bind properties to 'Properties' : prefix=test, ignoreInvalidFields=false, ignoreUnknownFields=true
18:56:05.073 [main] INFO org.apache.catalina.core.StandardService - Stopping service [Tomcat]
18:56:05.103 [main] INFO o.s.b.a.l.ConditionEvaluationReportLogger -
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
18:56:05.130 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'test.some-list' to com.google.common.collect.ImmutableList<java.lang.String>:
Property: test.some-list[0]
Value: "foo"
Origin: class path resource [application.yaml] - 6:7
Reason: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'test.some-list' to com.google.common.collect.ImmutableList<java.lang.String>
Action:
Update your application's configuration
Process finished with exit code 1
堆栈跟踪:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someBean' defined in file [****]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'test-test.spring.Properties': Could not bind properties to 'Properties' : prefix=test, ignoreInvalidFields=false, ignoreUnknownFields=true
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:245)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1352)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1189)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:942)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:436)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
at shared.bootstarters.microservice.SpringMicroservice.startAndInitialize(SpringMicroservice.kt:41)
at shared.bootstarters.microservice.SpringMicroservice.startAndInitialize$default(SpringMicroservice.kt:30)
at test.spring.MicroserviceKt.main(Microservice.kt:22)
Caused by: org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'test-test.spring.Properties': Could not bind properties to 'Properties' : prefix=test, ignoreInvalidFields=false, ignoreUnknownFields=true
at org.springframework.boot.context.properties.ConstructorBound.from(ConstructorBound.java:47)
at org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrar.lambda$createBeanDefinition$1(ConfigurationPropertiesBeanRegistrar.java:97)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:949)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:888)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
... 19 common frames omitted
Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'test.some-list' to com.google.common.collect.ImmutableList<java.lang.String>
at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:392)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:352)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$4(Binder.java:478)
at org.springframework.boot.context.properties.bind.ValueObjectBinder$ConstructorParameter.bind(ValueObjectBinder.java:318)
at org.springframework.boot.context.properties.bind.ValueObjectBinder.bind(ValueObjectBinder.java:76)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:482)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:596)
at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:582)
at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:480)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:419)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:348)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:337)
at org.springframework.boot.context.properties.bind.Binder.bindOrCreate(Binder.java:329)
at org.springframework.boot.context.properties.bind.Binder.bindOrCreate(Binder.java:314)
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bindOrCreate(ConfigurationPropertiesBinder.java:101)
at org.springframework.boot.context.properties.ConstructorBound.from(ConstructorBound.java:44)
... 35 common frames omitted
Caused by: java.lang.IllegalArgumentException: Could not instantiate Collection type: com.google.common.collect.ImmutableList
at org.springframework.core.CollectionFactory.createCollection(CollectionFactory.java:212)
at org.springframework.boot.context.properties.bind.CollectionBinder.lambda$bindAggregate$0(CollectionBinder.java:48)
at org.springframework.boot.context.properties.bind.AggregateBinder$AggregateSupplier.get(AggregateBinder.java:107)
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:111)
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:86)
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:70)
at org.springframework.boot.context.properties.bind.CollectionBinder.bindAggregate(CollectionBinder.java:49)
at org.springframework.boot.context.properties.bind.AggregateBinder.bind(AggregateBinder.java:56)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindAggregate$3(Binder.java:443)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:596)
at org.springframework.boot.context.properties.bind.Binder.bindAggregate(Binder.java:443)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:404)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:348)
... 49 common frames omitted
Caused by: java.lang.InstantiationException: null
at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.springframework.core.CollectionFactory.createCollection(CollectionFactory.java:208)
... 61 common frames omitted