Instantiate beans in order in Spring?

前端 未结 3 1638
礼貌的吻别
礼貌的吻别 2020-12-16 21:19

Is it possible to set order of instantiation in Spring?

I don\'t want to use @DependsOn and I don\'t want to use Ordered interface. I just

相关标签:
3条回答
  • 2020-12-16 21:29

    From @Order javadoc

    NOTE: Annotation-based ordering is supported for specific kinds of components only — for example, for annotation-based AspectJ aspects. Ordering strategies within the Spring container, on the other hand, are typically based on the Ordered interface in order to allow for programmatically configurable ordering of each instance.

    So I guess Spring just doesn't follow @Order() when creating beans.

    But if you just want to populate collections, maybe this is good enough for you:

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

    It won't change instantiation order, but you'll get ordered collections.

    0 讨论(0)
  • 2020-12-16 21:44

    If you want to make sure that a specific bean is created before another bean you can use the @DependsOn annotation.

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

    Keep in mind that this does not set the order, it only guarantees that the bean "foo" is created before "bar". JavaDoc for @DependsOn

    0 讨论(0)
  • 2020-12-16 21:46

    You can impose ordering in your example by first eliminating static on the classes MyBean1 and MyBean2, which when using Spring is not necessary in almost every case, as the default for Spring is to instantiate a single instance of each bean (similar to a Singleton).

    The trick is to declare MyBean1 and MyBean2 as @Bean and to enforce the order, you create an implicit dependency from bean1 to bean2 by calling bean2's bean initialization method from within bean1's initialization method.

    For example:

    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 讨论(0)
提交回复
热议问题