web-dev-qa-db-fra.com

Instancier les haricots dans l’ordre au printemps?

Est-il possible de définir l'ordre d'instanciation au printemps?

Je ne veux pas utiliser @DependsOn et je ne veux pas utiliser l'interface Ordered. J'ai juste besoin d'un ordre d'instanciation.

L'utilisation suivante de l'annotation @Order ne fonctionne pas:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

/**
 * Order does not work here
 */
public class OrderingOfInstantiation {

   public static class MyBean1 {{
      System.out.println(getClass().getSimpleName());
   }}

   public static class MyBean2 {{
      System.out.println(getClass().getSimpleName());
   }}

   @Configuration
   public static class Config {

      @Bean
      @Order(2)
      public MyBean1 bean1() {
         return new MyBean1();
      }

      @Bean
      @Order(1)
      public MyBean2 bean2() {
         return new MyBean2();
      }

   }

   public static void main(String[] args) {
      new AnnotationConfigApplicationContext(Config.class);
   }

}

Les haricots sont toujours instanciés dans l'ordre lexicographique.

Pourquoi ça ne marche pas ici?

Puis-je quand même compter sur l'ordre lexicographique?

METTRE À JOUR

Je souhaite toute solution permettant de fournir un ordre de création.

L'objectif est de renseigner les collections au niveau de la configuration dans le bon ordre. Depends on - ne correspond pas à la tâche. Toute "explication" sur la raison pour laquelle Spring n'aime pas avoir d'instanciations ordonnées - ne correspond pas non plus à la tâche.

Commande signifie commande :)

4
Dims

De @Order javadoc

REMARQUE: les commandes basées sur des annotations sont prises en charge pour des types de composants spécifiques uniquement - par exemple, pour les aspects AspectJ basés sur des annotations. Les stratégies de commande dans le conteneur Spring, en revanche, sont généralement basées sur l'interface Ordered afin de permettre la commande configurable par programme de chaque instance.

Donc, je suppose que le printemps ne suit tout simplement pas @Order() lors de la création de haricots.

Mais si vous souhaitez simplement renseigner des collections, cela vous suffit peut-être:

import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import org.Apache.commons.lang3.Tuple.Pair;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import Java.lang.annotation.*;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.List;

@Configuration
public class OrderingOfInstantiation {

    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(OrderingOfInstantiation.class);
    }

    @Component
    @CollectionOrder(collection = "myBeans", order = 1)
    public static class MyBean1 {{
        System.out.println(getClass().getSimpleName());
    }}

    @Component
    @CollectionOrder(collection = "myBeans", order = 2)
    public static class MyBean2 {{
        System.out.println(getClass().getSimpleName());
    }}

    @Configuration
    public static class CollectionsConfig {

        @Bean
        List<Object> myBeans() {
            return new ArrayList<>();
        }
    }

    // PopulateConfig will populate all collections beans
    @Configuration
    public static class PopulateConfig implements ApplicationContextAware {

        @SuppressWarnings("unchecked")
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            Multimap<String, Object> beansMap = MultimapBuilder.hashKeys().arrayListValues().build();

            // get all beans
            applicationContext.getBeansWithAnnotation(CollectionOrder.class)
                    .values().stream()

                    // get CollectionOrder annotation
                    .map(bean -> Pair.of(bean, bean.getClass().getAnnotation(CollectionOrder.class)))

                    // sort by order
                    .sorted((p1, p2) -> p1.getRight().order() - p2.getRight().order())

                    // add to multimap
                    .forEach(pair -> beansMap.put(pair.getRight().collection(), pair.getLeft()));

            // and add beans to collections
            beansMap.asMap().entrySet().forEach(entry -> {
                Collection collection = applicationContext.getBean(entry.getKey(), Collection.class);
                collection.addAll(entry.getValue());

                // debug
                System.out.println(entry.getKey() + ":");
                collection.stream()
                        .map(bean -> bean.getClass().getSimpleName())
                        .forEach(System.out::println);
            });
        }

    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    public @interface CollectionOrder {
        int order() default 0;

        String collection();
    }
}

Cela ne changera pas l'ordre d'instanciation, mais vous obtiendrez des collections ordonnées.

5
Yoav Aharoni

Si vous voulez vous assurer qu'un bean spécifique est créé avant un autre, vous pouvez utiliser l'annotation @DependsOn.

@Configuration
public class Configuration {

   @Bean 
   public Foo foo() {
   ...
   }

   @Bean
   @DependsOn("foo")
   public Bar bar() {
   ...
   }
}

Gardez à l'esprit que cela ne définit pas l'ordre, il garantit uniquement que le bean "foo" est créé avant "bar". JavaDoc pour @DependsOn

6
Richard

Vous pouvez imposer un ordre dans votre exemple en éliminant d’abord la statique dans les classes MyBean1 et MyBean2, ce qui n’est pas nécessaire lorsque vous utilisez Spring, dans la mesure où Spring utilise par défaut une instance unique de chaque bean (semblable à Singleton). .

L'astuce consiste à déclarer MyBean1 et MyBean2 en tant que @Bean et à appliquer l'ordre, vous créez une dépendance implicite de bean1 à bean2 en appelant la méthode d'initialisation de bean2 à partir de la méthode d'initialisation de bean1.

Par exemple:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

/**
 * Order does not work here
 */
public class OrderingOfInstantiation {

   interface MyBean{
       default String printSimpleName(){
           System.out.println(getClass().getSimpleName());
       }
   }

   public class MyBean1 implments MyBean{ 
       public MyBean1(){ pintSimpleName(); }
   }

   public class MyBean2 implments MyBean{ 
       public MyBean2(){ pintSimpleName(); }
   }

   public class MyBean3 implments MyBean{ 
       public MyBean3(){ pintSimpleName(); }
   }

   public class MyBean4 implments MyBean{ 
       public MyBean4(){ pintSimpleName(); }
   }

   public class MyBean5 implments MyBean{ 
       public MyBean5(){ pintSimpleName(); }
   }

   @Configuration
   public class Config {

      @Bean
      MyBean1 bean1() {
         //This will cause a a dependency on bean2
         //forcing it to be created before bean1
         bean2();

         return addToAllBeans(new MyBean1());
      }

      @Bean
      MyBean2 bean2() {
         //This will cause a a dependency on bean3
         //forcing it to be created before bean2
         bean3();

         //Note: This is added just to explain another point
         //Calling the bean3() method a second time will not create
         //Another instance of MyBean3. Spring only creates 1 by default
         //And will instead look up the existing bean2 and return that.
         bean3();

         return addToAllBeans(new MyBean2());
      }

      @Bean
      MyBean3 bean3(){ return addToAllBeans(new MyBean3()); }

      @Bean
      MyBean4 bean4(){ return addToAllBeans(new MyBean4()); }


      @Bean
      MyBean5 bean5(){ return addToAllBeans(new MyBean5()); }


      /** If you want each bean to add itself to the allBeans list **/
      @Bean
      List<MyBean> allBeans(){
          return new ArrayList<MyBean>();
      }

      private <T extends MyBean> T addToAllBeans(T aBean){
          allBeans().add(aBean);
          return aBean;
      }
   }

   public static void main(String[] args) {
      new AnnotationConfigApplicationContext(Config.class);
   }
}
0
pczeus