Inject bean into enum

47,762

Solution 1

public enum ReportType {

    REPORT_1("name", "filename"),
    REPORT_2("name", "filename");

    @Component
    public static class ReportTypeServiceInjector {
        @Autowired
        private DataPrepareService dataPrepareService;

        @PostConstruct
        public void postConstruct() {
            for (ReportType rt : EnumSet.allOf(ReportType.class))
               rt.setDataPrepareService(dataPrepareService);
        }
    }

[...]

}

weekens' answer works if you change inner class to static so spring can see it

Solution 2

Maybe something like this:

public enum ReportType {
    @Component
    public class ReportTypeServiceInjector {
        @Autowired
        private DataPrepareService dataPrepareService;

        @PostConstruct
        public void postConstruct() {
            for (ReportType rt : EnumSet.allOf(ReportType.class))
               rt.setDataPrepareService(dataPrepareService);
        }
    }

    REPORT_1("name", "filename"),
    REPORT_2("name", "filename"),
    ...
}

Solution 3

There is one another approach you may like to explore. However instead of injecting a bean into enum it associates a bean with an enum

Say you have an enum WidgetType and Widget class

public enum WidgetType {
  FOO, BAR;
}

public class Widget {

  WidgetType widgetType;
  String message;

  public Widget(WidgetType widgetType, String message) {
    this.widgetType = widgetType;
    this.message = message;
  }
}

And you want to create Widgets of this type using a Factory BarFactory or FooFactory

public interface AbstractWidgetFactory {
  Widget createWidget();
  WidgetType factoryFor();
}

@Component
public class BarFactory implements AbstractWidgetFactory {
  @Override
  public Widget createWidget() {
    return new Widget(BAR, "A Foo Widget");
  }
  @Override
  public WidgetType factoryFor() {
    return BAR;
  }
}

@Component
public class FooFactory implements AbstractWidgetFactory {
  @Override
  public Widget createWidget() {
    return new Widget(FOO, "A Foo Widget");
  }
  @Override
  public WidgetType factoryFor() {
    return FOO;
  }
}

The WidgetService is where most of the work happens. Here I have a simple AutoWired field which keeps tracks of all the registered WidgetFactories. As a postConstruct operation we create a map of the enum and the associated factory.

Now clients could inject the WidgetService class and get the factory for the given enum type

@Service
public class WidgetService {

  @Autowired
  List<AbstractWidgetFactory> widgetFactories;

  Map<WidgetType, AbstractWidgetFactory> factoryMap = new HashMap<>();

  @PostConstruct
  public void init() {
    widgetFactories.forEach(w -> {
      factoryMap.put(w.factoryFor(), w);
    });
  }

  public Widget getWidgetOfType(WidgetType widgetType) {
    return factoryMap.get(widgetType).createWidget();
  }

}

Solution 4

it will be hard to control that the spring container is already up and running at the time the enum is instantiated (if you had a variable with this type in a test-case, your container will usually not be there, even aspectj autowiring won't help there). i would recommend to just let the dataprepare-service or something give you the specific-params with a lookup-method with the enum-parameter.

Solution 5

Enums are static, so you have to figure out a way to access to the beans from a static context.

You can create a class named ApplicationContextProvider that implements the ApplicationContextAware interface.

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class ApplicationContextProvider implements ApplicationContextAware{

 private static ApplicationContext appContext = null;

 public static ApplicationContext getApplicationContext() {
   return appContext;
 }

 public void setApplicationContext(ApplicationContext appContext) throws BeansException {
   this.appContext = appContext;
 }
}

then add this your application context file:

<bean id="applicationContextProvider" class="xxx.xxx.ApplicationContextProvider"></bean>

after that you could access to the application context in a static way like this:

ApplicationContext appContext = ApplicationContextProvider.getApplicationContext();
Share:
47,762
Andrej Soroj
Author by

Andrej Soroj

Updated on December 07, 2020

Comments

  • Andrej Soroj
    Andrej Soroj over 3 years

    I have the DataPrepareService that prepare data for reports and I have an Enum with report types, and I need to inject ReportService into Enum or have access to ReportService from enum.

    my service:

    @Service
    public class DataPrepareService {
        // my service
    }
    

    my enum:

    public enum ReportType {
    
        REPORT_1("name", "filename"),
        REPORT_2("name", "filename"),
        REPORT_3("name", "filename")
    
        public abstract Map<String, Object> getSpecificParams();
    
        public Map<String, Object> getCommonParams(){
            // some code that requires service
        }
    }
    

    I tried to use

    @Autowired
    DataPrepareService dataPrepareService;
    

    , but it didn't work

    How can I inject my service into enum?