Guava ImmutableBiMap成为LinkedHashMap并导致Spring自动装配错误

时间:2017-01-09 13:57:07

标签: java spring guava

我有ImmutableBiMap填充了2个简单的Spring bean。

操作系统:Manjaro Linux JDK版本:1.8.0.102 Oracle 春季版: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。

enter image description here

最小,完整且可验证的示例:

@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;
    }
}

2 个答案:

答案 0 :(得分:4)

这是how Spring handles some container types的副作用。

  

只要预期的密钥类型是,即使是键入的Map也可以自动装配   StringMap值将包含预期类型的​​所有bean,   并且键将包含相应的bean名称:[...]

BiMapMap

Spring并没有尝试将automatons bean注入TestControl。相反,它试图找到类型Integer的所有bean作为值,将它们收集到MapLinkedHashMap作为选择的实现),并将它们与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 +。

  

对于自身定义为集合/映射或数组的bean   类型,@Resource是一个很好的解决方案,指的是具体的   集合或数组bean按唯一名称。也就是说,截至4.3,   可以通过Spring匹配集合/映射和数组类型   @Autowired类型匹配算法也是如此,只要元素   类型信息保存在@Bean返回类型签名或   集合继承层次结构。在这种情况下,限定符值可以   用于在相同类型的集合中进行选择,如中所述   前一段。

我会对你在4.3之前看到的行为感到满意,但这似乎是Map的错误。 (List和数组类型的行为正确。)

我已经打开SPR-15117来跟踪它,现在已经解决了(2天营业额,哇!)。

答案 1 :(得分:1)

除非Spring中存在巨大的错误(我怀疑),否则这必定是人/编辑错误。

我重新创建了一个更简单的例子,同样的基础知识我刚刚使用了String,Integer,Long和Boolean,因为我没有你的类型 - 这个简单的例子可以运行。

LinkedHashMap不是BiMap,如果它被选为autowire候选者,那将是一个错误。它几乎听起来像源代码和已编译的代码不同步,您是否尝试删除构建文件夹并重建?

如果重建没有帮助,解决这个问题的唯一方法就是老式调试。

  1. 在LinkedHashMaps构造函数中放置一个断点并查看它的构造位置,它与您的bean有什么关系吗?
  2. 设置条件断点(因此只有在org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor中beanName.equals(&#34; automatonTypeSettingsControl&#34;)才会停止,并逐步执行该方法,以便您可以看到春天如何找到autowire候选人;
  3. 制作最简单的独立示例,将其置于Github并发布链接,然后其他人可以帮助您进行调试。
  4. 观察:我在上个月看过很多StackOverflow帖子,看起来平均开发人员调试第三方代码不是很好。实际上,你可以从调试其他人的代码中学到很多东西,尤其是spring框架代码,考虑到它正在解决的问题,我觉得这很容易阅读。

    编辑这在Spring中是一个限制,如另一个答案中所述。这就是说我最终重现错误并通过Spring代码读取,以便在大约1小时内找到此行为的确切代码。我觉得很多开发人员都把调试视为软件学科。对我而言,它是最重要的学科之一,因为你可能花了大部分时间来处理你自己没有写过的代码。