Using Spring IoC to set up enum values
OK, it's quite fiddly, but it CAN be done.
It's true that Spring cannot instantiate enums. But that's not a problem - Spring can also use factory methods.
This is the key component:
public class EnumAutowiringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private final List<Class<? extends Enum>> enumClasses = new ArrayList<>();
public EnumAutowiringBeanFactoryPostProcessor(Class<? extends Enum>... enumClasses) {
Collections.addAll(this.enumClasses, enumClasses);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (Class<? extends Enum> enumClass : enumClasses) {
for (Enum enumVal : enumClass.getEnumConstants()) {
BeanDefinition def = new AnnotatedGenericBeanDefinition(enumClass);
def.setBeanClassName(enumClass.getName());
def.setFactoryMethodName("valueOf");
def.getConstructorArgumentValues().addGenericArgumentValue(enumVal.name());
((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(enumClass.getName() + "." + enumVal.name(), def);
}
}
}
}
Then the following test class shows that it works:
@Test
public class AutowiringEnumTest {
public void shouldAutowireEnum() {
new AnnotationConfigApplicationContext(MyConig.class);
assertEquals(AutowiredEnum.ONE.myClass.field, "fooBar");
assertEquals(AutowiredEnum.TWO.myClass.field, "fooBar");
assertEquals(AutowiredEnum.THREE.myClass.field, "fooBar");
}
@Configuration
public static class MyConig {
@Bean
public MyClass myObject() {
return new MyClass("fooBar");
}
@Bean
public BeanFactoryPostProcessor postProcessor() {
return new EnumAutowiringBeanFactoryPostProcessor(AutowiredEnum.class);
}
}
public enum AutowiredEnum {
ONE,
TWO,
THREE;
@Resource
private MyClass myClass;
}
public static class MyClass {
private final String field;
public MyClass(String field) {
this.field = field;
}
}
}
I don't think it can be done from Spring's ApplicationContext
configuration. But, do you really need it done by Spring, or can you settle for simple externalization using ResourceBundle; like this:
public enum Car
{
NANO,
MERCEDES,
FERRARI;
public final String cost;
public final String madeIn;
Car()
{
this.cost = BUNDLE.getString("Car." + name() + ".cost");
this.madeIn = BUNDLE.getString("Car." + name() + ".madeIn");
}
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(...);
}
In the properties file, one for each specific locale, enter the keys describing the possible internal enum values:
Car.NANO.cost=Very cheap
Car.NANO.madeIn=India
Car.MERCEDES.cost=Expensive
...
The only drawback of this approach is having to repeat the name of enum fields (cost, madeIn) in Java code as strings. Edit: And on the plus side, you can stack all properties of all enums into one properties file per language/locale.
Do you mean setting up the enum
itself?
I don't think that's possible. You cannot instantiate enums because they have a static
nature. So I think that Spring IoC can't create enums
as well.
On the other hand, if you need to set initialize something with a enum
please check out the Spring IoC chapter. (search for enum) There's a simple example that you can use.
Why not provide a setter (or constructor argument) that takes a String, and simply call Enum.valueOf(String s) to convert from a String to an enum. Note an exception will get thrown if this fails, and your Spring initialisation will bail out.