1

From this question, it is possible to inject map with enums?

For example, i have enum:

class enum SomeEnum (val value) {
  ONE("one"),
  TWO("two"),
  THREE("three")
}

And i have some interface with implementations:

interface SomeInterface {
}

@Component
@Qualifier("one")
class OneClass: SomeInterface {
...
}

@Component
@Qualifier("two")
class TwoClass: SomeInterface {
...
}

@Component
@Qualifier("three")
class ThreeClass: SomeInterface {
...
}

But such injection not works:

@Component
@ConfigurationProperties
class SomeProperties {
   @Autowired
   lateinit var someMap: Map<SomeEnum, SomeInterface>
}

I want to autoinject someMap. How can i fix it, to make such code on spring framework side?

var someMap: Map<SomeEnum, SomeInterface> = Map.of(ONE, oneClass,
                                                   TWO, twoClass,
                                                   THREE, threeClass)

// Where oneClass, twoClass, threeClass - beans instances
SlandShow
  • 443
  • 3
  • 13

2 Answers2

1

Not quite sure what you want to do, but from my point of view you dont need this mapping. I assume you want to know which implementation to use for certain case. So just autowire a list or set of your interface and iterate trough it to find the right implementation. (I show the stuff in Java)

@Autowired
List<SomeInterface> someInterfaces;

In this list you will have all the injected implementations of this interface. If you still need an Enum to see which implementation to use, just add this as an attribute to every of your implementation class. So you can get the Enum value by its implementation.

EDIT: Create a config class and autowire the list of implementations. In this config class you create a Bean of your map.

@Configuration
public class MyMapConfig {

    @Autowired
    List<SomeInterface> someInterfaces;

    @Bean
    public Map<SomeEnum, SomeInterface> myMap() {
        Map<SomeEnum, SomeInterface> map = new HashMap<>();
        someInterfaces.forEach(s -> {
            // logic here to add correct Enum to its implementation.
            map.put(SomeEnum.A, s);
        });
        return map;

    }

    public enum SomeEnum {
        A, B, C
    }
}

Then you can autowire your map anywhere you want:

@Autowired
Map<SomeEnum, SomeInterface> myMap;
Patrick
  • 12,336
  • 15
  • 73
  • 115
1
  1. First of all you misuse @Qualifier annotation. It's intended not to name the bean but to help Spring to choose single autowire candidate among multiple. @Qualifier is only meaningful at injection point - either in pair with @Autowired for class properties
@Autowired
@Qualifier("one")
lateinit var someImpl: SomeInterface

or for injection into method/constructor arguments

@Bean
fun createSomeService(@Qualifier("two") someImpl: SomeInterface): SomeService {
   return SomeService(someImpl)
}

//or

class SomeService(@Qualifier("three") private val someImpl: SomeInterface) { 
    ... 
}

Correct way to name a bean is simply @Component("name") (or @Service("name"))

  1. As far as I know, you can only autowire Maps with String keys, where key is the name of the bean. However you can easily convert this map into enum-key map like this:
interface SomeInterface

@Component("one")
class OneClass: SomeInterface

@Component("two")
class TwoClass: SomeInterface

@Component("three")
class ThreeClass: SomeInterface

@Component
@ConfigurationProperties
class SomeProperties(implMap: Map<String, SomeInterface>) {

    val someMap: Map<SomeEnum, SomeInterface> = implMap.mapKeys {
        SomeEnum.values().firstOrNull { e -> e.value == it.key }!!
    }
}

UPDATED:

or using field injection (not recommended)

@Component
@ConfigurationProperties
class SomeProperties {
    private lateinit var someMap: Map<SomeEnum, SomeInterface>

    @Autowired
    private lateinit var implMap: Map<String, SomeInterface>

    @PostConstruct
    fun initMap() {
        someMap = implMap.mapKeys {
            SomeEnum.values().firstOrNull { e -> e.value == it.key }!!
        } 
    }
}
Nikolai Shevchenko
  • 7,083
  • 8
  • 33
  • 42