Java 将 bean 注入枚举

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16318454/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:17:30  来源:igfitidea点击:

Inject bean into enum

javaspringdependency-injectionautowired

提问by Andrej Soroj

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.

我有 DataPrepareService 为报告准备数据,我有一个带有报告类型的 Enum,我需要将 ReportService 注入 Enum 或从枚举访问 ReportService。

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?

如何将我的服务注入枚举?

回答by weekens

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

回答by cproinger

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.

很难控制在枚举实例化时 spring 容器已经启动并运行(如果你在测试用例中有一个这种类型的变量,你的容器通常不会在那里,即使是aspectj自动装配也不会)在那里帮忙)。我建议只让 dataprepare-service 或其他东西为您提供带有枚举参数的查找方法的特定参数。

回答by user3195004

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' answerworks if you change inner class to static so spring can see it

如果您将内部类更改为静态以便 spring 可以看到它,那么weekens 的答案会起作用

回答by Josema

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

Enums 是静态的,所以你必须想办法从静态上下文访问 bean。

You can create a class named ApplicationContextProviderthat implements the ApplicationContextAwareinterface.

您可以创建一个名为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;
 }
}

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

回答by Ashish Shetkar

I think this what you need

我认为这是你需要的

public enum MyEnum {
    ONE,TWO,THREE;
}

Autowire the enum as per usual

像往常一样自动装配枚举

@Configurable
public class MySpringConfiguredClass {

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

}

Here is the trick, use the factory-method="valueOf" and also make sure lazy-init="false"

这是诀窍,使用 factory-method="valueOf" 并确保 lazy-init="false"

so the container creates the bean upfront

所以容器预先创建了 bean

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

and you are done!

你就完成了!

回答by Wanja Krah

Just pass it to the method manually

只需将其手动传递给方法

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

As long as you call the method only from managed beans, you can inject it in these beans and pass the reference to the enum on each call.

只要您仅从托管 bean 调用该方法,您就可以将其注入这些 bean 并在每次调用时传递对枚举的引用。

回答by diduknow

There is one another approach you may like to explore. However instead of injecting a beaninto enumit associates a beanwith an enum

您可能想探索另一种方法。但是,不是将 abean注入enum其中,而是将abeanenum

Say you have an enum WidgetTypeand Widgetclass

假设你有一个枚举WidgetTypeWidget

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 BarFactoryor FooFactory

并且您想Widget使用 FactoryBarFactoryFooFactory

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 WidgetServiceis where most of the work happens. Here I have a simple AutoWiredfield which keeps tracks of all the registered WidgetFactories. As a postConstructoperation we create a map of the enum and the associated factory.

WidgetService是大部分工作发生的地方。在这里,我有一个简单的AutoWired字段,用于跟踪所有已注册的WidgetFactories。作为postConstruct操作,我们创建了枚举和关联工厂的映射。

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

现在客户可以注入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();
  }

}

回答by Ersoy Ko?ak

Maybe you can use this solution ;

也许你可以使用这个解决方案;

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

And in another class which you need the bean of the Enum :

在另一个类中,您需要 Enum 的 bean:

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