Подтвердить что ты не робот

Вставить bean в перечисление

У меня есть DataPrepareService, который готовит данные для отчетов, и у меня есть Enum с типами отчетов, и мне нужно добавить ReportService в Enum или получить доступ к ReportService из перечисления.

мой сервис:

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

Я попытался использовать

@Autowired
DataPrepareService dataPrepareService;

но это не сработало

Как я могу ввести свою службу в перечисление?

4b9b3361

Ответ 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);
        }
    }

[...]

}

Ответ на выходные работает, если вы меняете внутренний класс на статичный, чтобы spring его видна

Ответ 2

Возможно, что-то вроде этого:

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

Ответ 3

будет трудно контролировать, что контейнер spring уже запущен и запущен во время создания перечисления (если у вас была переменная с этим типом в тестовом случае, ваш контейнер, как правило, не будет там, даже aspectj autowiring там не поможет). я бы рекомендовал просто позволить службе dataprepare или что-то дать вам специфические параметры с помощью метода lookup с параметром enum.

Ответ 4

Есть еще один подход, который вы можете изучить. Однако вместо того, чтобы вводить bean в enum он связывает bean с enum

Скажем, у вас есть класс WidgetType и Widget enum

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 этого типа с помощью Factory BarFactory или 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;
  }
}

WidgetService - это место, где происходит большая часть работы. Здесь у меня есть простое поле AutoWired котором хранятся следы всех зарегистрированных WidgetFactor. В качестве операции postConstruct мы создаем карту перечисления и соответствующей фабрики.

Теперь клиенты могут вводить класс 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();
  }

}

Ответ 5

Enum являются статическими, поэтому вам нужно выяснить способ доступа к beans из статического контекста.

Вы можете создать класс с именем 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();

Ответ 6

Я думаю, что вам нужно

public enum MyEnum {
    ONE,TWO,THREE;
}

Autowire enum как обычно

@Configurable
public class MySpringConfiguredClass {

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

}

Вот трюк, используйте factory -method = "valueOf", а также убедитесь, что ленивый-INIT = "ложь"

поэтому контейнер создает bean аванс

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

и все готово!

Ответ 7

Возможно, вы можете использовать это решение;

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

И в другом классе, который вам нужен bean of Enum:

ChartTypes plotType = ChartTypes.AreaChart
Object areaChartService = applicationContext.getBean(chartType.getServiceName());

Ответ 8

Просто передайте его методу вручную

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(DataPrepareService  dataPrepareService){
        // some code that requires service
    }
}

До тех пор, пока вы вызываете метод только из управляемых компонентов, вы можете вставлять его в эти компоненты и передавать ссылку на перечисление для каждого вызова.