it-swarm.com.de

Bohnen im Frühling in Ordnung bringen?

Ist es möglich, die Reihenfolge der Instanziierung im Frühjahr festzulegen?

Ich möchte @DependsOn nicht verwenden und ich möchte die Ordered-Schnittstelle nicht verwenden. Ich brauche nur eine Instanziierungsreihenfolge.

Die folgende Verwendung der Annotation @Order funktioniert nicht:

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

}

Bohnen werden immer noch in lexikografischer Reihenfolge instanziiert.

Warum klappt es hier nicht?

Kann ich mich trotzdem auf die lexikografische Reihenfolge verlassen?

UPDATE

Ich hätte gerne eine Lösung, die es mir erlaubt, die Reihenfolge der Erstellung festzulegen.

Das Ziel ist es, Sammlungen auf Konfigurationsebene in der richtigen Reihenfolge zu füllen. Depends on - stimmt nicht mit der Aufgabe überein. Alle "Erklärungen", warum Spring keine Instanziierungen bestellt haben möchte - passen auch nicht zur Aufgabe.

Ordnung heißt Ordnung :)

4
Dims

Von @Order javadoc

ANMERKUNG: Die auf Anmerkungen basierende Reihenfolge wird unterstützt nur für bestimmte Arten von Komponenten - zum Beispiel für auf Anmerkungen basierende Aspekte von AspectJ. Auf der anderen Seite basieren die Bestellstrategien im Spring-Container normalerweise auf der Bestellschnittstelle, um eine programmgesteuert konfigurierbare Reihenfolge für jede Instanz zu ermöglichen.

Ich schätze, Spring folgt beim Erstellen von Beans einfach nicht @Order().

Aber wenn Sie nur Sammlungen füllen möchten, ist dies vielleicht gut genug für Sie:

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

Die Instantiierungsreihenfolge wird nicht geändert, es werden jedoch Sammlungen bestellt.

5
Yoav Aharoni

Wenn Sie sicherstellen möchten, dass eine bestimmte Bean vor einer anderen Bean erstellt wird, können Sie die Annotation @DependsOn verwenden.

@Configuration
public class Configuration {

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

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

Denken Sie daran, dass dies nicht die Reihenfolge festlegt, sondern nur garantiert, dass die Bohne "foo" vor "bar" erstellt wird. JavaDoc für @DependsOn

6
Richard

Sie können die Reihenfolge in Ihrem Beispiel festlegen, indem Sie zunächst die statischen Werte für die Klassen MyBean1 und MyBean2 beseitigen, was bei Verwendung von Spring in fast allen Fällen nicht erforderlich ist, da Spring standardmäßig eine einzelne Instanz jeder Bean instanziiert (ähnlich wie bei einem Singleton). .

Der Trick besteht darin, MyBean1 und MyBean2 als @Bean zu deklarieren und die Reihenfolge durchzusetzen. Sie erstellen eine implizite Abhängigkeit von bean1 zu bean2, indem Sie die Bean-Initialisierungsmethode von bean2 innerhalb der Initialisierungsmethode von bean1 aufrufen.

Zum Beispiel:

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