简体   繁体   English

在Spring中按顺序实例化bean?

[英]Instantiate beans in order in Spring?

Is it possible to set order of instantiation in Spring? 是否可以在Spring中设置实例化顺序?

I don't want to use @DependsOn and I don't want to use Ordered interface. 我不想使用@DependsOn而且我不想使用Ordered接口。 I just need an order of instantiation. 我只需要一个实例化的命令。

The following usage of @Order annotation does not work: @Order注释的以下用法不起作用:

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

}

Beans are still instantiated in lexicographic order. Bean仍按字典顺序实例化。

Why it does not work here? 为什么它在这里不起作用?

Can I rely of lexicographic order anyway? 我还能依靠词典顺序吗?

UPDATE UPDATE

I would like any solution allowing to provide order of creation. 我想任何允许提供创作顺序的解决方案。

The goal is to populate collections at config level in correct order. 目标是以正确的顺序在配置级别填充集合。 Depends on -- does not match the task. Depends on - 与任务不匹配。 Any "explanations" on why Spring does not like to have ordered instantiations -- also does not match the task. 关于为什么Spring不喜欢有序实例化的任何“解释” - 也与该任务不匹配。

Order means order :) 订单意味着订单:)

From @Order javadoc 来自@Order javadoc

NOTE: Annotation-based ordering is supported for specific kinds of components only — for example, for annotation-based AspectJ aspects. 注意: 仅对特定类型的组件支持基于注释的排序 - 例如,对于基于注释的AspectJ方面。 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. 另一方面,Spring容器中的排序策略通常基于Ordered接口,以便允许以编程方式配置每个实例的顺序。

So I guess Spring just doesn't follow @Order() when creating beans. 所以我猜Spring在创建bean时并不遵循@Order()

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. 它不会改变实例化顺序,但是你会获得有序的集合。

If you want to make sure that a specific bean is created before another bean you can use the @DependsOn annotation. 如果要确保在另一个bean之前创建特定bean,可以使用@DependsOn批注。

@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". 请记住,这不会设置顺序,它只保证在“bar”之前创建bean“foo”。 JavaDoc for @DependsOn @DependsOn的JavaDoc

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). 您可以通过首先消除类MyBean1MyBean2上的静态来强加您的示例中的排序,在几乎每种情况下都不需要使用Spring,因为Spring的默认设置是实例化每个bean的单个实例(类似于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. 诀窍是将MyBean1MyBean2声明为@Bean并强制执行顺序,通过从bean1的初始化方法中调用bean2的bean初始化方法,创建从bean1到bean2的隐式依赖项。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM