SpringBoot系列教程之Bean加载顺序之错误使用姿势辟谣
在网上查询 Bean 的加载顺序时,看到了大量的文章中使用@Order
注解的方式来控制 bean 的加载顺序,不知道写这些的博文的同学自己有没有实际的验证过,本文希望通过指出这些错误的使用姿势,让观文的小伙伴可以知道@Order
的具体的应用场景
I. 环境搭建
创建一个 maven 项目,pom 文件如下(具体的项目代码,可以在文末获取)
<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
,一个@AutoConfigureOrder
I. @Order
err.case1: 类上添加 Order 注解
一种常见的错误观点是在类上添加这个 Order 注解,就可以指定 bean 之间的初始化顺序,order 值越小,则优先级越高,接下来我们实际测试一下,是否如此
我们创建两个 DemoBean, 指定不同的 Order 顺序
@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 应该先被初始化,实际测试一下,输出如下
err.case2: 配置类中 Bean 声明方法上添加@Order
Bean 除了上面的自动扫描之外,还有一种方式就是通过@Bean
注解,下面我们演示一下在配置类中指定 bean 加载顺序的错误 case
同样我们新建两个测试 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 应该在前面
根据上面的输出,也可以看出列表中的顺序和我们预期的一致,并且 AnoOrderBean1
与 AnoOrderBean2
的加载顺序和注解没有关系
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. 项目
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 源码模块: - https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/008-beanorder - https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/008-beanorder-addition - https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/008-beanorder-addition2
1. 一灰灰 Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰 Blog 个人博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top
SpringBoot系列教程之Bean加载顺序之错误使用姿势辟谣的更多相关文章
- SpringBoot系列教程之Bean之指定初始化顺序的若干姿势
上一篇博文介绍了@Order注解的常见错误理解,它并不能指定 bean 的加载顺序,那么问题来了,如果我需要指定 bean 的加载顺序,那应该怎么办呢? 本文将介绍几种可行的方式来控制 bean 之间 ...
- Spring 系列教程之 bean 的加载
Spring 系列教程之 bean 的加载 经过前面的分析,我们终于结束了对 XML 配置文件的解析,接下来将会面临更大的挑战,就是对 bean 加载的探索.bean 加载的功能实现远比 bean 的 ...
- SpringBoot中的bean加载顺序
https://www.dazhuanlan.com/2019/10/22/5daebc5d16429/ 最近在做传统Spring项目到SpringBoot项目迁移过程中,遇到了一些bean加载顺序的 ...
- springboot由于bean加载顺序导致的问题
先记录现象: dubbo整合zipkin时,我的配置文件是这样的 @Bean("okHttpSender") public OkHttpSenderFactoryBean okHt ...
- 3springboot:springboot配置文件(外部配置加载顺序、自动配置原理,@Conditional)
1.外部配置加载顺序 SpringBoot也可以从以下位置加载配置: 优先级从高到低 高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置 1.命令行参数 所有的配置都可以在命令行上进行指定 ...
- 【spring】bean加载顺序
问题来源 有一个bean为A,一个bean为B.想要A在容器实例化的时候的一个属性name赋值为B的一个方法funB的返回值. 如果只是在A里单纯的写着: private B b; private S ...
- spring bean加载顺序指定方式之一
在某些情况下,我们在容器启动的时候做一些事情,举个例子,加载缓存等.. 此时我们会希望某个bean先被加载并执行其中的afterpropertiesset方法. 因为spring默认是通过contex ...
- SpringBoot系列教程之Redis集群环境配置
之前介绍的几篇redis的博文都是基于单机的redis基础上进行演示说明的,然而在实际的生产环境中,使用redis集群的可能性应该是大于单机版的redis的,那么集群的redis如何操作呢?它的配置和 ...
- 如果你还不知道如何控制springboot中bean的加载顺序,那你一定要看此篇
1.为什么需要控制加载顺序 springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题.在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功 ...
随机推荐
- Qt for Android开发入门
1. Qt for Android环境搭建 1.1 打包需要的工具 1.2 JDK安装 如果之前配置过,就可以跳过这一步. 下载java jdk 64:java jdk 1.8 x64 ...
- Channel使用技巧
前言 Go协程一般使用channel(通道)通信从而协调/同步他们的工作.合理利用Go协程和channel能帮助我们大大提高程序的性能.本文将介绍一些使用channel的场景及技巧 场景一,使用cha ...
- demo演示如何写一个无配置格式统一的日志
一.背景 大量项目在使用logback记日志,有部分项目使用日志混乱,格式不统一,多数人搞不懂配置文件,导致配置错误,现在需要开发一套统一的.少配置的日志组件,方便使用. 二.设计思路 尽量采用0配置 ...
- Apache和Tomcat 配置负载均衡(mod-proxy方式)-无session共享、无粘性session
转:https://blog.csdn.net/wangjunjun2008/article/details/38268483 mod-proxy方式实现负载均衡是利用了Apache 2.x版本自带的 ...
- 004-python面向对象,错误,调试和测试
---恢复内容开始--- 1.面向对象 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作 ...
- Python集训营45天—Day08 (文件操作)
目录 1. 文件操作介绍 2. 文件的读写 2.1 文本文件 2.2 二进制文件 2.3 JSON文件 3. 文件的定位,重命名和删除 4. 文件夹的相关操作 1. 文件操作介绍 大家应该听说过一句话 ...
- QQ第三方授权登录OAuth2.0实现(Java)
准备材料 1.已经备案好的域名 2.服务器(域名和服务器为统一主体或域名已接入服务器) 3.QQ号 4.开发流程:https://wiki.connect.qq.com/%E5%87%86%E5%A4 ...
- 从0开始学FreeRTOS-(列表&列表项)-6
# FreeRTOS列表&列表项的源码解读 第一次看列表与列表项的时候,感觉很像是链表,虽然我自己的链表也不太会,但是就是感觉很像. 在FreeRTOS中,列表与列表项使用得非常多,是Free ...
- 线程池和lambda表达式
线程池1.什么是线程池.一个用来创建和管理线程的容器;2.线程池的作用.提高线程的复用性,降低资源消耗提高线程的响应速度,提高线程的可管理性3.线程的核心思想;线程的复用 4.线程池的创建Execut ...
- 代码审计-thinkphp3.2.3框架漏洞sql注入
开始复现审计一下tp3和tp5的框架漏洞,当个练习吧. 涉及注入的方法为where() table() delete()等. 环境 tp3.2.3 : 0x01 注入成因 测试代码: public f ...