web-dev-qa-db-ja.com

Beanをenumに注入します

レポート用のデータを準備するDataPrepareServiceがあり、レポートタイプを含むEnumがあり、EnumにReportServiceを挿入するか、EnumからReportServiceにアクセスする必要があります。

私のサービス:

@Service
public class DataPrepareService {
    // my service
}

私の列挙型:

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

使ってみた

@Autowired
DataPrepareService dataPrepareService;

、しかしうまくいきませんでした

私のサービスをenumに注入するにはどうすればよいですか?

53
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 内部クラスをstaticに変更すると、春が見えるようになります

58
user3195004

たぶんこのようなもの:

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

あなたが探検したいもう一つのアプローチがあります。ただし、beanenumに挿入する代わりに、beanenumに関連付けます。

Enum WidgetTypeおよび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;
  }
}

そして、ファクトリWidgetまたはBarFactoryを使用して、このタイプのFooFactorysを作成したい

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

WidgetServiceは、ほとんどの作業が行われる場所です。ここには、登録済みのすべてのAutoWirediesを追跡する簡単なWidgetFactorフィールドがあります。 postConstruct操作として、enumおよび関連するファクトリーのマップを作成します。

クライアントはWidgetServiceクラスを挿入し、指定された列挙型のファクトリーを取得できるようになりました

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

}
2
diduknow

enumがインスタンス化される時点で、スプリングコンテナーが既に起動して実行されていることを制御するのは困難です(テストケースでこのタイプの変数があった場合、通常、コンテナーは存在しません。そこに役立つ)。 dataprepare-serviceか、enum-parameterを含むlookup-methodを使用してspecific-paramsを提供することをお勧めします。

1
cproinger

たぶん、このソリューションを使用できます。

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

そして、EnumのBeanが必要な別のクラスで:

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

Enumsは静的なので、静的コンテキストからBeanにアクセスする方法を理解する必要があります。

ApplicationContextProviderインターフェイスを実装するApplicationContextAwareという名前のクラスを作成できます。

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

次に、これをアプリケーションコンテキストファイルに追加します。

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

その後、次のような静的な方法でアプリケーションコンテキストにアクセスできます。

ApplicationContext appContext = ApplicationContextProvider.getApplicationContext();
0
Josema

これはあなたが必要なものだと思う

public enum MyEnum {
    ONE,TWO,THREE;
}

通常どおり列挙を自動配線します

@Configurable
public class MySpringConfiguredClass {

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

}

ここにトリックがあります。factory-method= "valueOf"を使用し、lazy-init = "false"を確認してください。

コンテナはBeanを前もって作成します

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

これで完了です!

0
Ashish Shetkar