web-dev-qa-db-fra.com

Injecter le haricot dans l'énum

J'ai le DataPrepareService qui prépare les données pour les rapports et j'ai un Enum avec des types de rapport et je dois injecter ReportService dans Enum ou avoir accès à ReportService à partir d'énum.

mon service:

@Service
public class DataPrepareService {
    // my service
}

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

J'ai essayé d'utiliser 

@Autowired
DataPrepareService dataPrepareService;

, mais ça n'a pas marché

Comment puis-je injecter mon service dans enum?

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

[...]

}

La réponse de weekens fonctionne si vous changez la classe intérieure en statique afin que le printemps puisse la voir

50
user3195004

Peut-être quelque chose comme ça:

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

il sera difficile de contrôler que le conteneur de ressort est déjà opérationnel au moment de l'instanciation de l'énumération (si vous aviez une variable de ce type dans un scénario de test, votre conteneur ne serait généralement pas là, même si le gain de temps était atteint t aider là-bas). Je recommanderais simplement de laisser le service dataprepare ou quelque chose qui vous donne les paramètres spécifiques avec une méthode de recherche avec le paramètre enum. 

1
cproinger

Il existe une autre approche que vous voudrez peut-être explorer. Cependant, au lieu d’injecter une bean dans enum, il associe une bean à une enum 

Disons que vous avez un enum WidgetType et Widget class

public enum WidgetType {
  FOO, BAR;
}

public class Widget {

  WidgetType widgetType;
  String message;

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

Et vous voulez créer Widgets de ce type en utilisant une usine BarFactory ou 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;
  }
}

La WidgetService est l'endroit où la majeure partie du travail est effectuée. Ici, j'ai un simple champ AutoWired qui garde une trace de tous les WidgetFactories enregistrés. En tant qu’opération postConstruct, nous créons une carte de l’énum et de l’usine associée. 

Désormais, les clients peuvent injecter la classe WidgetService et obtenir l’usine pour le type d’énum donné.

@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

Peut-être que vous pouvez utiliser cette 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;
}
}

Et dans une autre classe dont vous avez besoin du haricot de l'énum:

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

Enums sont statiques, vous devez donc trouver un moyen d'accéder aux beans à partir d'un contexte statique.

Vous pouvez créer une classe nommée ApplicationContextProvider qui implémente l'interface 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;
 }
}

puis ajoutez ceci votre fichier de contexte d'application:

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

après cela, vous pouvez accéder au contexte de l'application de manière statique comme ceci:

ApplicationContext appContext = ApplicationContextProvider.getApplicationContext();
0
Josema

Je pense que ce dont tu as besoin

public enum MyEnum {
    ONE,TWO,THREE;
}

Autowire l'énum comme d'habitude

@Configurable
public class MySpringConfiguredClass {

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

}

Voici le truc, utilisez la méthode-usine = "valueOf" et assurez-vous également que Lazy-init = "false"

de sorte que le conteneur crée le haricot dès le départ

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

et vous avez terminé!

0
Ashish Shetkar