Guava ImmutableBiMap 变为 LinkedHashMap 并导致 Spring 自动装配错误
Guava ImmutableBiMap becomes LinkedHashMap and cause Spring autowiring mistake
我有 ImmutableBiMap 填充了 2 个简单的 Spring bean。
OS:曼扎罗 Linux
JDK版本:1.8.0.102甲骨文
Spring 版本:4.3.4.RELEASE 来自
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Athens-SR1</version>
创建上下文抛出:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Illegal arguments for constructor; nested exception is java.lang.IllegalArgumentException: argument type mismatch
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
如下图所示,当Spring的BeanUtil参数抛出异常时,参数是LinkedHashMap而不是BiMap。
最小、完整且可验证的示例:
@Component
@Slf4j
public class TestControl {
private final BiMap<String, Integer> automatons;
@Autowired
public TestControl(BiMap<String, Integer> automatons) {
this.automatons = automatons;
log.info("automatons={}", automatons.keySet());
}
}
@Configuration
public class TextContext {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(
TextContext.class,
TestControl.class
);
BiMap bean = context.getBean(BiMap.class);
}
@Bean
BiMap<String, Integer> automatons() {
return ImmutableBiMap.of(
"Cellular Automaton", cellularAutomaton(),
"Monte Carlo Automaton", monteCarloAutomaton());
}
@Bean
Integer cellularAutomaton() {
return 6;
}
@Bean
Integer monteCarloAutomaton() {
return 5;
}
}
除非 Spring 中存在巨大错误(我对此表示怀疑),否则这一定是 human/editor 错误。
我重新创建了一个稍微简单一些的示例,与我刚刚使用的字符串、整数、长整型和布尔值相同的基础知识,因为我没有您的类型 - 这个简单的示例有效。
LinkedHashMap 不是 BiMap,所以如果它被选为自动装配候选者,那将是一个错误。听起来好像源代码和编译代码不同步,您是否尝试过删除构建文件夹并重建?
如果重建没有帮助,解决这个问题的唯一方法就是老式调试。
- 在LinkedHashMaps的构造函数里面打个断点,看看是在哪里构造的,跟你的bean有关系吗?
- 在org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor 中设置一个条件断点(所以你只在beanName.equals("automatonTypeSettingsControl")时停止,然后逐步执行该方法,这样你就可以看到spring 找到自动装配候选人;
- 做一个失败的最简单的独立例子,把它放在Github和post上link,然后其他人可能会帮你调试。
观察:上个月看了很多Whosebugpost,看来一般的开发者不太擅长调试第三方代码。你实际上可以从调试别人的代码中学到很多东西,尤其是 spring 框架代码,考虑到它正在解决的问题,我觉得它很容易阅读。
Edit 这原来是 Spring 中的限制,如另一个答案中所述。也就是说,我最终重现了错误并阅读了 Spring 代码以在大约 1 小时内找到此行为的确切代码。我觉得许多开发人员忽视了调试作为一门软件学科。对我来说,这是最重要的学科之一,因为您可能大部分时间都在处理不是您自己编写的代码。
这是 how Spring handles some container types 的副作用。
Even typed Map
s can be autowired as long as the expected key type is
String
. The Map
values will contain all beans of the expected type,
and the keys will contain the corresponding bean names: [...]
一个BiMap
是一个Map
。
Spring 并没有尝试将您的 automatons
bean 注入 TestControl
。相反,它试图找到所有 Integer
类型的 beans 作为值,将它们收集到 Map
(LinkedHashMap
作为选择的实现),并将它们与它们的 bean 名称相关联作为键.
在这种情况下,它失败了,因为构造函数需要 BiMap
。
一种解决方案是按名称注入。
@Autowired()
public TestControl(@Qualifier(value = "automatons") BiMap<String, Integer> automatons) {
this.automatons = automatons;
}
通过指定带有名称的限定符,Spring 将改为尝试查找名为 automatons
的 bean(具有适当的类型)。
如果你不太依赖 final
实例字段,你也可以用 @Resource
注入该字段
@Resource(name = "automatons") // if you don't specify the name element, Spring will try to use the field name
private BiMap<String, Integer> automatons;
出于原因,这仅适用于 4.3+。
For beans that are themselves defined as a collection/map or array
type, @Resource
is a fine solution, referring to the specific
collection or array bean by unique name. That said, as of 4.3,
collection/map and array types can be matched through Spring’s
@Autowired
type matching algorithm as well, as long as the element
type information is preserved in @Bean
return type signatures or
collection inheritance hierarchies. In this case, qualifier values can
be used to select among same-typed collections, as outlined in the
previous paragraph.
我可以接受您在 4.3 之前的版本中看到的行为,但这对于 Map
来说确实像是一个错误。 (正确的行为发生在 List
和数组类型。)
我已经打开SPR-15117跟踪了,现在已经解决了(2天成交量,哇!)。
我有 ImmutableBiMap 填充了 2 个简单的 Spring bean。
OS:曼扎罗 Linux JDK版本:1.8.0.102甲骨文 Spring 版本:4.3.4.RELEASE 来自
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Athens-SR1</version>
创建上下文抛出:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Illegal arguments for constructor; nested exception is java.lang.IllegalArgumentException: argument type mismatch
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
如下图所示,当Spring的BeanUtil参数抛出异常时,参数是LinkedHashMap而不是BiMap。
最小、完整且可验证的示例:
@Component
@Slf4j
public class TestControl {
private final BiMap<String, Integer> automatons;
@Autowired
public TestControl(BiMap<String, Integer> automatons) {
this.automatons = automatons;
log.info("automatons={}", automatons.keySet());
}
}
@Configuration
public class TextContext {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(
TextContext.class,
TestControl.class
);
BiMap bean = context.getBean(BiMap.class);
}
@Bean
BiMap<String, Integer> automatons() {
return ImmutableBiMap.of(
"Cellular Automaton", cellularAutomaton(),
"Monte Carlo Automaton", monteCarloAutomaton());
}
@Bean
Integer cellularAutomaton() {
return 6;
}
@Bean
Integer monteCarloAutomaton() {
return 5;
}
}
除非 Spring 中存在巨大错误(我对此表示怀疑),否则这一定是 human/editor 错误。
我重新创建了一个稍微简单一些的示例,与我刚刚使用的字符串、整数、长整型和布尔值相同的基础知识,因为我没有您的类型 - 这个简单的示例有效。
LinkedHashMap 不是 BiMap,所以如果它被选为自动装配候选者,那将是一个错误。听起来好像源代码和编译代码不同步,您是否尝试过删除构建文件夹并重建?
如果重建没有帮助,解决这个问题的唯一方法就是老式调试。
- 在LinkedHashMaps的构造函数里面打个断点,看看是在哪里构造的,跟你的bean有关系吗?
- 在org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor 中设置一个条件断点(所以你只在beanName.equals("automatonTypeSettingsControl")时停止,然后逐步执行该方法,这样你就可以看到spring 找到自动装配候选人;
- 做一个失败的最简单的独立例子,把它放在Github和post上link,然后其他人可能会帮你调试。
观察:上个月看了很多Whosebugpost,看来一般的开发者不太擅长调试第三方代码。你实际上可以从调试别人的代码中学到很多东西,尤其是 spring 框架代码,考虑到它正在解决的问题,我觉得它很容易阅读。
Edit 这原来是 Spring 中的限制,如另一个答案中所述。也就是说,我最终重现了错误并阅读了 Spring 代码以在大约 1 小时内找到此行为的确切代码。我觉得许多开发人员忽视了调试作为一门软件学科。对我来说,这是最重要的学科之一,因为您可能大部分时间都在处理不是您自己编写的代码。
这是 how Spring handles some container types 的副作用。
Even typed
Map
s can be autowired as long as the expected key type isString
. TheMap
values will contain all beans of the expected type, and the keys will contain the corresponding bean names: [...]
一个BiMap
是一个Map
。
Spring 并没有尝试将您的 automatons
bean 注入 TestControl
。相反,它试图找到所有 Integer
类型的 beans 作为值,将它们收集到 Map
(LinkedHashMap
作为选择的实现),并将它们与它们的 bean 名称相关联作为键.
在这种情况下,它失败了,因为构造函数需要 BiMap
。
一种解决方案是按名称注入。
@Autowired()
public TestControl(@Qualifier(value = "automatons") BiMap<String, Integer> automatons) {
this.automatons = automatons;
}
通过指定带有名称的限定符,Spring 将改为尝试查找名为 automatons
的 bean(具有适当的类型)。
如果你不太依赖 final
实例字段,你也可以用 @Resource
@Resource(name = "automatons") // if you don't specify the name element, Spring will try to use the field name
private BiMap<String, Integer> automatons;
出于原因,这仅适用于 4.3+。
For beans that are themselves defined as a collection/map or array type,
@Resource
is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, collection/map and array types can be matched through Spring’s@Autowired
type matching algorithm as well, as long as the element type information is preserved in@Bean
return type signatures or collection inheritance hierarchies. In this case, qualifier values can be used to select among same-typed collections, as outlined in the previous paragraph.
我可以接受您在 4.3 之前的版本中看到的行为,但这对于 Map
来说确实像是一个错误。 (正确的行为发生在 List
和数组类型。)
我已经打开SPR-15117跟踪了,现在已经解决了(2天成交量,哇!)。