08-02-2023, 04:48 PM
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;
}
}
}
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;
}
}
}