43

I've an Enum class

public enum MyEnum{
    ABC;
}

than my 'Mick' class has this property

private Map<MyEnum, OtherObj> myMap;

I've this spring xml configuration.

<util:map id="myMap">
    <entry key="ABC" value-ref="myObj" />
</util:map>

<bean id="mick" class="com.x.Mick">
    <property name="myMap" ref="myMap" />
</bean>

and this is fine.
I'd like to replace this xml configuration with Spring annotations.
Do you have any idea on how to autowire the map?

The problem here is that if I switch from xml config to the @Autowired annotation (on the myMap attribute of the Mick class) Spring is throwing this exception

nested exception is org.springframework.beans.FatalBeanException: Key type [class com.MyEnum] of map [java.util.Map] must be assignable to [java.lang.String]

Spring is no more able to recognize the string ABC as a MyEnum.ABC object.
Any idea?

Thanks

mickthompson
  • 5,442
  • 11
  • 47
  • 59
  • It's not clear what you're trying to do. What sort of annotations are you thinking of? – skaffman Jul 24 '09 at 16:44
  • I'd like to use the @Autowired annotation but it's not working. Do I have to specify something else to tell Spring to treat that Key value as an Enum instead of a String? – mickthompson Jul 26 '09 at 11:53

7 Answers7

53

This worked for me...

My Spring application context:

<util:map id="myMap">
  <entry key="#{T(com.acme.MyEnum).ELEM1}" value="value1" />
  <entry key="#{T(com.acme.MyEnum).ELEM2}" value="value2" />
</util:map>

My class where the Map gets injected:

public class MyClass {

    private @Resource Map<MyEnum, String> myMap;
}

The important things to note are that in the Spring context I used SpEL (Spring Expression Language) which is only available since version 3.0. And in my class I used @Resource, neither @Inject (it didn't work for me) nor @Autowired (I didn't try this). The only difference I'm aware of between @Resource and @Autowired, is that the former auto-inject by bean name while the later does it by bean type.

Enjoy!

Cristian Ebbens
  • 626
  • 7
  • 3
  • 10
    Remember to use '$' instead of '.' for separating an inner enum from an outer class in Spring EL. – Amir Moghimi Feb 08 '11 at 04:40
  • 8
    Just a follow up that `@Autowired` will also yield `Key type [class com.foo.Bar$BAZ] of map [java.util.Map] must be assignable to [java.lang.String]`, `@Resource` is the winner. +1 @Amir, `$` is a gotcha. – markdsievers Aug 28 '11 at 18:53
  • Question said "I'd like to *replace* this xml configuration with Spring annotations." yet there's still config XML here. Does that mean you can't do this in Spring purely with annotations? – Jonik Nov 15 '11 at 14:03
  • 1
    @Jonik I think the replacement intended was the bean and resource injection part, not the map part. Otherwise, I'm not sure why this answer was selected. – Patrick Dec 11 '12 at 23:07
  • Thank you very much for this. Only @ Resource works. I tried @ Autowired and it did not work. Strange, but I'll just go with @ Resource – Sanjiv Jivan Mar 04 '14 at 14:57
18

This one gave me fits but I was able to piece it together using David's answer and some other links (below).

  • do not change the names of the properties in the MapFactoryBean declaration.
  • ensure that key-type attribute points to the enum that you want to use as a key in the map.

Class

@Component
public class MyClass {

    private Map<MyEnum, ValueObjectInterface> valueMap;

    @Autowired
    public void setValueMap(final Map<MyEnum, ValueObjectInterface> valueMap) {
        this.valueMap= valueMap;
    }


}

Enum

    public enum MyEnum{
    FOO ("FOO"),
    BAR ("BAR"),
    BAZ ("BAZ");
}

XML Config file:

<bean id="valueMap" class="org.springframework.beans.factory.config.MapFactoryBean">
    <property name="targetMapClass">
        <value>java.util.HashMap</value>
    </property>
    <property name="sourceMap">
        <map key-type="com.company.packagepath.MyEnum">
          <entry key="FOO" value-ref="valueObject1" />
          <entry key="BAR" value-ref="valueObject2" />
          <entry key="BAZ" value-ref="valueObject3" />
        </map>
    </property>
</bean>

<bean id="valueObject1"  class="com.company.packagepath.ValueObject1" />
<bean id="valueObject2"  class="com.company.packagepath.ValueObject2" />
<bean id="valueObject3"  class="com.company.packagepath.ValueObject3" />

LINKS

Community
  • 1
  • 1
Tony R
  • 7,136
  • 4
  • 21
  • 12
5

Application context

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd ">

<bean id="myProvider" class="com.project.MapProvider">
    <property name="myMap" ref="myMap"/>
</bean>

<util:map id="myMap" key-type="com.project.MyEnum" value-type="com.project.ValueObject">
    <entry>
        <key><value type="com.project.MyEnum">FOO</value></key>
        <ref bean="objectValue1"/>
    </entry>
</util:map>
</beans>

Java class

package com.project;

public class MapProvider {

    private Map<MyEnum, ValueObject> myMap;

    public void setMyMap(Map<MyEnum, ValueObject> myMap) {
        this.myMap = myMap;
    }
}
1

Should be:

public class Mick {

  private Map<MyEnum, OtherObj> myMap;

  @Autowired
  public void setMyMap(Map<MyEnum, OtherObj> myMap) {
    this.myMap = myMap;
  }
}

Have a look at http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config

Updated

The problem is that according to the util schema, you cannot specify the key or value types. You can however to implement a MapFactoryBean of your own (just inherit from org.springframework.beans.factory.config.MapFactoryBean). One ceveat - notice that the generic definition (even thought erased in runtime) doesn't get in the way.

David Rabinowitz
  • 29,904
  • 14
  • 93
  • 125
  • Hi David, I know about the @Autowired annotation. Here the problem is that if I autowire the map Spring is no more able to recognize the string ABC as a MyEnum.ABC object. With XML configuration it works fine, with annotations configuration it's throwing this Exception nested exception is org.springframework.beans.FatalBeanException: Key type [class com.MyEnum] of map [java.util.Map] must be assignable to [java.lang.String] – mickthompson Jul 26 '09 at 11:46
0

The <util:map> element has key-type, resp. value-type attributes, that represents the class of the keys, resp. the values. If you specify the fully qualified class of your enum in the key-type attribute, the keys are then parsed into that enum when creating the map.

Spring verifies during injection that the map's key and value types -as declared in the class containing the map- are assignment-compatible with the key and value types of the map bean. This is actually where you get the exception from.

Gaetan
  • 2,802
  • 2
  • 21
  • 26
0

You just need to use concrete Map class as HashMap and not abstract or interface:

public class Mick {

  private HashMap<MyEnum, OtherObj> myMap;

  @Autowired
  public void setMyMap(HashMap<MyEnum, OtherObj> myMap) {
    this.myMap = myMap;
  }
}


public class AppConfig
{
    @Bean
    public HashMap<MyEnum, OtherObj> myMap() { .. }
}
Olga Pshenichnikova
  • 1,509
  • 4
  • 22
  • 47
0

If you have a Map with an Enum values as keys, then consider using Java's EnumMap implementation:

https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/EnumMap.html

Here you also have a Baeldung post with some examples on how to use it:

https://www.baeldung.com/java-enum-map

Gerardo Roza
  • 2,746
  • 2
  • 24
  • 33