【问题标题】:Inject bean into enum将bean注入枚举
【发布时间】:2013-04-25 11:15:23
【问题描述】:

我有为报告准备数据的 DataPrepareService,我有一个带有报告类型的 Enum,我需要将 ReportService 注入 Enum 或从 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;

,但是没有用

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

【问题讨论】:

    标签: java spring dependency-injection autowired


    【解决方案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 如果将内部类更改为静态,则可以使用,以便 spring 可以看到它

    【讨论】:

    • 你拯救了我的一天。谢谢!
    • 这里需要注意的是,上面的代码不会编译,因为静态类是在枚举常量之前定义的。根据java规范,枚举常量在任何类声明之前。简单地把常量放在上面,把你的类放在下面。
    • 别忘了将它作为 bean 添加到您的 config.xml
    【解决方案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】:

    您可能还想探索另一种方法。然而,不是将bean 注入到enum 中,而是将beanenum 关联起来

    假设你有一个枚举 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;
      }
    }
    

    并且您想使用工厂BarFactoryFooFactory 创建这种类型的Widgets

    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 字段,它跟踪所有注册的WidgetFactories。作为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();
      }
    
    }
    

    【讨论】:

    • WidgetService 是一个单例,factoryMap 不应该是像 f.e. 这样的不可变映射的实例。 java.lang.Object.com.google.common.collect.ImmutableMap&lt;K,V&gt; ?
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        我认为这是你需要的

        public enum MyEnum {
            ONE,TWO,THREE;
        }
        

        像往常一样自动装配枚举

        @Configurable
        public class MySpringConfiguredClass {
        
                  @Autowired
              @Qualifier("mine")
                  private MyEnum myEnum;
        
        }
        

        这是诀窍,使用 factory-method="valueOf" 并确保 懒惰初始化=“假”

        所以容器会预先创建 bean

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

        你就完成了!

        【讨论】:

          【解决方案7】:

          只需手动将其传递给方法

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

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

          【讨论】:

            【解决方案8】:

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

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

            【讨论】:

            • 问题显然是如何将依赖项注入枚举。这没有回答。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-08-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-21
            • 1970-01-01
            相关资源
            最近更新 更多