SpringBoot系列教程之Bean加载顺序之错误使用姿式辟谣

在网上查询 Bean 的加载顺序时,看到了大量的文章中使用@Order注解的方式来控制 bean 的加载顺序,不知道写这些的博文的同窗本身有没有实际的验证过,本文但愿经过指出这些错误的使用姿式,让观文的小伙伴能够知道@Order的具体的应用场景java

原文地址: SpringBoot系列教程之Bean加载顺序之错误使用姿式辟谣git

I. 环境搭建

建立一个 maven 项目,pom 文件以下(具体的项目代码,能够在文末获取)github

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7</version>
    <relativePath/> <!-- lookup parent from update -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
复制代码

II. 错误姿式

下面咱们会介绍两种典型注解的错误使用姿式,一个@Order,一个@AutoConfigureOrderweb

I. @Order

err.case1: 类上添加 Order 注解

一种常见的错误观点是在类上添加这个 Order 注解,就能够指定 bean 之间的初始化顺序,order 值越小,则优先级越高,接下来咱们实际测试一下,是否如此spring

咱们建立两个 DemoBean, 指定不一样的 Order 顺序bash

@Order(4)
@Component
public class BaseDemo1 {
    private String name = "base demo 1";

    public BaseDemo1() {
        System.out.println(name);
    }
}

@Order(3)
@Component
public class BaseDemo2 {
    private String name = "base demo 2";

    public BaseDemo2() {
        System.out.println(name);
    }
}
复制代码

根据前面的观点,orde 值小的优先级高,那么 BaseDemo2 应该先被初始化,实际测试一下,输出以下maven

err.case2: 配置类中 Bean 声明方法上添加@Order

Bean 除了上面的自动扫描以外,还有一种方式就是经过@Bean注解,下面咱们演示一下在配置类中指定 bean 加载顺序的错误 casespring-boot

一样咱们新建两个测试 bean学习

public class BaseDemo3 {
    private String name = "base demo 3";

    public BaseDemo3() {
        System.out.println(name);
    }
}

public class BaseDemo4 {
    private String name = "base demo 4";

    public BaseDemo4() {
        System.out.println(name);
    }
}
复制代码

接下来在配置类中定义 bean测试

@Configuration
public class ErrorDemoAutoConf {
    @Order(2)
    @Bean
    public BaseDemo3 baseDemo3() {
        return new BaseDemo3();
    }

    @Order(1)
    @Bean
    public BaseDemo4 baseDemo4() {
        return new BaseDemo4();
    }
}
复制代码

一样的,若是@Order注解有效,那么BaseDemo4应该先被初始化

从上面的实际测试输出能够看出,@Order 注解在上面的方式中也不生效,若是有兴趣的同窗能够试一下,将上面配置类中的两个方法的顺序颠倒一下,会发现BaseDemo4先加载

err.case3: @Order 注解修饰配置类

这也是一种常见的错误 case,认为@Order 注解是用来指定配置类的加载顺序的,然而真的是这样么?

咱们建立两个测试的配置类

@Order(1)
@Configuration
public class AConf {
    public AConf() {
        System.out.println("AConf init!");
    }
}

@Order(0)
@Configuration
public class BConf {
    public BConf() {
        System.out.println("BConf init");
    }
}
复制代码

若是@Order 注解生效,那么 BConf 配置类会优先初始化,那么咱们实测一下

从上面的结果能够看出,并非 BConf 先被加载;固然这种使用姿式,实际上和第一种错误 case,并无什么区别,配置类也是 bean,前面不生效,这里固然也不会生效

那么是否是咱们的理解不对致使的呢,实际上这个@Order放在配置类上以后,是这个配置类中定义的 Bean 的优先于另外一个配置类中定义的 Bean 呢?

一样的咱们测试下这种 case,咱们定义三个 bean,两个 conf

public class Demo1 {
    private String name = "conf demo bean 1";

    public Demo1() {
        System.out.println(name);
    }
}

public class Demo2 {
    private String name = "conf demo bean 2";

    public Demo2() {
        System.out.println(name);
    }
}

public class Demo3 {
    private String name = "conf demo bean 3";

    public Demo3() {
        System.out.println(name);
    }
}
复制代码

而后咱们将 Demo1, Demo3 放在一个配置中,Demo2 放在另一个配置中

@Order(2)
@Configuration
public class AConf1 {
    @Bean
    public Demo1 demo1() {
        return new Demo1();
    }

    @Bean
    public Demo3 demo3() {
        return new Demo3();
    }
}

@Order(1)
@Configuration
public class BConf1 {

    @Bean
    public Demo2 demo2() {
        return new Demo2();
    }
}
复制代码

若是@Order 注解实际上控制的是配置类中 Bean 的加载顺序,那么 BConf1 中的 Bean 应该优先加载,也就是说 Demo2 会优先于 Demo1, Demo3,实际测试一下,输出如

上面的输出结果和咱们预期的并不同,因此@Order注解来决定配置类的顺序也是不对的

2. @AutoConfigureOrder

从命名来看,这个注解是用来指定配置类的顺序的,然而对于这个注解的错误使用也是很是多的,而大多的错误使用在于没有真正的了解到它的使用场景

接下来咱们来演示一下错误的使用 case

在工程内新建两个配置类,直接使用注解

@Configuration
@AutoConfigureOrder(1)
public class AConf2 {
    public AConf2() {
        System.out.println("A Conf2 init!");
    }
}

@Configuration
@AutoConfigureOrder(-1)
public class BConf2 {
    public BConf2() {
        System.out.println("B conf2 init!");
    }
}
复制代码

当注解生效时,BConf 会优先级加载

从输出结果来看,和咱们预期的不同;那么这个注解是否是做用于配置类中的 Bean 的顺序,而不是配置类自己呢?

一样的咱们设计一个 case 验证一下

public class DemoA {
    private String name = "conf demo bean A";

    public DemoA() {
        System.out.println(name);
    }
}

public class DemoB {
    private String name = "conf demo bean B";

    public DemoB() {
        System.out.println(name);
    }
}

public class DemoC {
    private String name = "conf demo bean C";

    public DemoC() {
        System.out.println(name);
    }
}
复制代码

对应的配置类

@Configuration
@AutoConfigureOrder(1)
public class AConf3 {
    @Bean
    public DemoA demoA() {
        return new DemoA();
    }

    @Bean
    public DemoC demoC() {
        return new DemoC();
    }
}

@Configuration
@AutoConfigureOrder(-1)
public class BConf3 {

    @Bean
    public DemoB demoB() {
        return new DemoB();
    }
}
复制代码

若是 DemoB 后被加载,则说明上面的观点是错误的,实测结果以下

因此问题来了,@AutoConfigureOrder这个注解并不能指定配置类的顺序,还叫这个名,干啥?存粹是误导人不是!!!

接下来咱们看一下@Order@AutoConfigureOrder的正确使用方式

III. 使用说明

1. @Order

先看一下这个注解的官方注释

{@code @Order} defines the sort order for an annotated component. Since Spring 4.0, annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their {@code @Bean} method). While such order values may influence priorities at injection points, please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).

最开始 Order 注解用于切面的优先级指定;在 4.0 以后对它的功能进行了加强,支持集合的注入时,指定集合中 bean 的顺序

而且特别指出了,它对于但实例的 bean 之间的顺序,没有任何影响;这句话根据咱们上面的测试也能够验证

接下来咱们须要看一下经过@Order 注解来注入集合时,指定顺序的场景

首先咱们定义两个 Bean 实现同一个接口,并添加上@Order注解

public interface IBean {
}

@Order(2)
@Component
public class AnoBean1 implements IBean {

    private String name = "ano order bean 1";

    public AnoBean1() {
        System.out.println(name);
    }
}

@Order(1)
@Component
public class AnoBean2 implements IBean {

    private String name = "ano order bean 2";

    public AnoBean2() {
        System.out.println(name);
    }
}
复制代码

而后再一个测试 bean 中,注入IBean的列表,咱们须要测试这个列表中的 Bean 的顺序是否和咱们定义的@Order规则一致

@Component
public class AnoTestBean {

    public AnoTestBean(List<IBean> anoBeanList) {
        for (IBean bean : anoBeanList) {
            System.out.println("in ano testBean: " + bean.getClass().getName());
        }
    }
}
复制代码

根据咱们的预期, anoBeanList 集合中,anoBean2 应该在前面

根据上面的输出,也能够看出列表中的顺序和咱们预期的一致,而且 AnoOrderBean1AnoOrderBean2 的加载顺序和注解没有关系

2. @AutoConfigureOrder

这个注解用来指定配置文件的加载顺序,然而前面的测试中并无生效,那么正确的使用姿式是怎样的呢?

@AutoConfigureOrder适用于外部依赖的包中 AutoConfig 的顺序,而不能用来指定本包内的顺序

为了验证上面的说法,咱们再次新建两个工程,并指定自动配置类的顺序

工程一配置以下:

@AutoConfigureOrder(1)
@Configuration
@ComponentScan(value = {"com.git.hui.boot.order.addition"})
public class AdditionOrderConf {
    public AdditionOrderConf() {
        System.out.println("additionOrderConf init!!!");
    }
}
复制代码

注意自动配置类如要被正确加载,须要在工程的 /META-INF/spring.factories文件中定义

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.git.hui.boot.order.addition.AdditionOrderConf
复制代码

工程二的配置以下:

@Configuration
@AutoConfigureOrder(-1)
@ComponentScan("com.git.hui.boot.order.addition2")
public class AdditionOrderConf2 {

    public AdditionOrderConf2() {
        System.out.println("additionOrderConf2 init!!!");
    }
}
复制代码

而后咱们在项目内部添加一个配置

@AutoConfigureOrder(10)
@Configuration
public class OrderConf {
    public OrderConf() {
        System.out.println("inner order conf init!!!");
    }
}
复制代码

由于注解适用于外部依赖包中的自动配置类的顺序,因此上面三个配置类中,正确的话 AdditionOrderConf2 在 AdditionOrderConf1 以前;而 OrderConf 并不会收到注解的影响,默认环境下,内部定义的配置类会优于外部依赖,从下面的输出也能够佐证咱们说明(固然为了验证确实如次,还应该调整下两个外部工程配置类的顺序,并观察下加载顺序是否随之改变,咱们这里省略掉了)

IV. 小结

本篇主要介绍了网上对@Order@AutoConfigureOrder常见的错误使用姿式,并给出了正确的使用 case。

下面用简单的几句话介绍一下正确的姿式

  • @Order注解不能指定 bean 的加载顺序,它适用于 AOP 的优先级,以及将多个 Bean 注入到集合时,这些 bean 在集合中的顺序
  • @AutoConfigureOrder指定外部依赖的 AutoConfig 的加载顺序(即定义在/META-INF/spring.factories文件中的配置 bean 优先级),在当前工程中使用这个注解并无什么鸟用
  • 一样的 @AutoConfigureBefore@AutoConfigureAfter这两个注解的适用范围和@AutoConfigureOrder同样

0. 项目

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛

一灰灰blog
相关文章
相关标签/搜索