it-swarm.com.de

Bean in Enum injizieren

Ich habe den DataPrepareService, der Daten für Berichte vorbereitet, und ich habe ein Enum mit Berichtstypen. Ich muss ReportService in Enum injizieren oder Zugriff auf ReportService von Enum haben.

mein Dienst:

@Service
public class DataPrepareService {
    // my service
}

meine 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
    }
}

Ich habe versucht zu benutzen 

@Autowired
DataPrepareService dataPrepareService;

, aber es hat nicht funktioniert

Wie kann ich meinen Dienst in enum eintragen?

45
Andrej Soroj
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 funktioniert, wenn Sie die innere Klasse in statisch ändern, damit der Frühling sie sehen kann

50
user3195004

Vielleicht so etwas:

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"),
    ...
}
9
weekens

es ist schwer zu kontrollieren, dass der Federcontainer zum Zeitpunkt der Aufzählung der Enumeration bereits in Betrieb ist (wenn Sie in einem Testfall eine Variable mit diesem Typ hatten, wird Ihr Container normalerweise nicht dort sein. dort helfen). Ich würde empfehlen, einfach den Dataprepare-Service oder etwas anderes mit einer Lookup-Methode mit dem Enum-Parameter die spezifischen -Params angeben zu lassen. 

1
cproinger

Es gibt einen anderen Ansatz, den Sie erkunden möchten. Anstatt jedoch eine bean in enum zu injizieren, wird eine bean einer enum zugeordnet. 

Angenommen, Sie haben eine Enumeration WidgetType und Widget

public enum WidgetType {
  FOO, BAR;
}

public class Widget {

  WidgetType widgetType;
  String message;

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

Und Sie möchten Widgets dieses Typs mit einer Factory BarFactory oder FooFactory erstellen.

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;
  }
}

Die WidgetService ist der Ort, an dem die meiste Arbeit stattfindet. Hier habe ich ein einfaches AutoWired-Feld, das alle registrierten WidgetFactories verfolgt. Als postConstruct-Operation erstellen wir eine Karte der Enumeration und der zugehörigen Factory. 

Nun könnten Clients die WidgetService-Klasse einspritzen und die Factory für den angegebenen Aufzählungstyp erhalten

@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();
  }

}
1
diduknow

Vielleicht können Sie diese Lösung verwenden.

public enum ChartTypes {
AREA_CHART("Area Chart", XYAreaChart.class),
BAR_CHART("Bar Chart", XYBarChart.class),

private String name;
private String serviceName;

ChartTypes(String name, Class clazz) {
    this.name = name;
    this.serviceName = clazz.getSimpleName();
}

public String getServiceName() {
    return serviceName;
}

@Override
public String toString() {
    return name;
}
}

Und in einer anderen Klasse, in der Sie die Bohne des Enums brauchen:

ChartTypes plotType = ChartTypes.AreaChart
Object areaChartService = applicationContext.getBean(chartType.getServiceName());
0
Ersoy Koçak

Da Enums statisch sind, müssen Sie einen Weg finden, um aus einem statischen Kontext auf die Beans zuzugreifen.

Sie können eine Klasse mit dem Namen ApplicationContextProvider erstellen, die die ApplicationContextAware-Schnittstelle implementiert. 

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;
 }
}

fügen Sie dann Ihre Anwendungskontextdatei hinzu:

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

danach konnten Sie auf statische Weise auf den Anwendungskontext wie folgt zugreifen:

ApplicationContext appContext = ApplicationContextProvider.getApplicationContext();
0
Josema

Ich denke das brauchst du

public enum MyEnum {
    ONE,TWO,THREE;
}

Autowire die Enum wie gewohnt

@Configurable
public class MySpringConfiguredClass {

          @Autowired
      @Qualifier("mine")
          private MyEnum myEnum;

}

Hier ist der Trick, verwenden Sie die factory-method = "valueOf" und stellen Sie sicher, dass Lazy-init = "false"

der Container erstellt also die Bohne im Voraus

<bean id="mine" class="foo.bar.MyEnum" factory-method="valueOf" lazy-init="false">
    <constructor-arg value="ONE" />
</bean>

und du bist fertig!

0
Ashish Shetkar