Java 在Spring中按顺序实例化bean?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36187063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:32:57  来源:igfitidea点击:

Instantiate beans in order in Spring?

javaspringannotations

提问by Dims

Is it possible to set order of instantiation in Spring?

是否可以在 Spring 中设置实例化顺序?

I don't want to use @DependsOnand I don't want to use Orderedinterface. I just need an order of instantiation.

我不想使用@DependsOn,也不想使用Ordered界面。我只需要一个实例化的顺序。

The following usage of @Orderannotation 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

更新

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. Any "explanations" on why Spring does not like to have ordered instantiations -- also does not match the task.

目标是以正确的顺序在配置级别填充集合。Depends on-- 与任务不匹配。关于为什么 Spring 不喜欢有序实例化的任何“解释”——也与任务不匹配。

Order means order :)

订单意味着订单:)

采纳答案by Yoav Aharoni

From @Orderjavadoc

来自@Orderjavadoc

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.

注意:仅特定类型的组件支持基于注解的排序——例如,基于注解的 AspectJ 方面。另一方面,Spring 容器内的排序策略通常基于 Ordered 接口,以便允许以编程方式配置每个实例的排序。

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

所以我猜@Order()在创建 bean 时不会遵循 Spring 。

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.

它不会改变实例化顺序,但你会得到有序的集合。

回答by Richard

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". JavaDoc for @DependsOn

请记住,这不会设置顺序,它仅保证在“bar”之前创建 bean“foo”。 @DependsOn 的 JavaDoc

回答by pczeus

You can impose ordering in your example by first eliminating static on the classes MyBean1and 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).

您可以通过首先消除类MyBean1和上的静态来在示例中强加排序MyBean2,这在使用 Spring 时几乎在所有情况下都不是必需的,因为 Spring 的默认设置是实例化每个 bean 的单个实例(类似于单例)。

The trick is to declare MyBean1and MyBean2as @Beanand 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.

关键是要申报MyBean1,并MyBean2作为@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);
   }
}