目录
1、@Order 注解改变Bean自动注入的顺序
1.1、了解@SpringBootTest注解
1.2、@Order 注解改变Bean自动注入的顺序
2、@DependsOn 改变Bean的创建顺序
1、@Order 注解改变Bean自动注入的顺序
在sping中,通过IOC(控制反转)和DI(依赖注入)这种机制,可以将对象的依赖注入到该对象中,而不是在对象内部直接创建依赖。以达到降低耦合度,提高代码的可维护性和灵活性的目的。
1.1、了解@SpringBootTest注解
@SpringBootTest(classes = 配置类名称.class)
基本等同于启动了整个服务,此时便可以开始功能测试。
当@SpringBootTest没有指定配置类的时候,默认是把@SpringBootApplication注解下的类作为配置类,然后就会去扫描这个默认配置类所在的包,原因如下图所示
这里例子里@SpringBootApplication注解下的类所在包为:com.lt,要扫描这个包,把这个包下配置的bean加载到spring容器中去,这些bean才会生效。
(1)如果注解@SpringBootTest(classes = 配置类名称.class)中指定了默认项目配置类,则该测试类可以放在test.java下任何包中,比如:
图中的测试类(C3IocApplicationTests)的注解@SpringBootTest指定了C3IocApplication(默认项目配置类)为配置类,这时候测试类(C3IocApplicationTests)就可以放在test.java下任何包中
(2)如果注解@SpringBootTest没有配置里面的参数classes = C3IocApplication(默认项目配置类),则需要确保test.java下的测试类所在的包与默认项目配置类所在的包一致,即在test.java下也需要创建com.lt包,并将测试类放在该包下,比如:
图中的测试类(C3IocApplicationTests)的注解@SpringBootTest没有配置里面的参数classes = C3IocApplication(默认项目配置类),所以这里test.java下的测试类(C3IocApplicationTests)必须与默认配置类(C3IocApplication)所在的包(com.lt)一致,都是在com.lt这个包下
1.2、@Order 注解改变Bean自动注入的顺序
注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响。所以这里说改变Bean的自动注入的顺序就是改变Bean的执行顺序。
相关代码
//接口
public interface I {
}
//接口I的实现类A
public class A implements I{
public A(){
System.out.println("A");
}
}
//接口I的实现类A
public class B implements I{
public B(){
System.out.println("B");
}
}
//测试类TestOrder
//@SpringBootTest(classes = 启动类名称.class),这里配置了TestOrder.class为项目启动类
@SpringBootTest(classes = TestOrder.class)
public class TestOrder {
@Bean
public A a(){
return new A();
}
@Bean
public B b(){
return new B();
}
@Test
public void test(@Autowired List<I> i){
//这里不用List集合的话就只能获取一个I的bean,
//但是I是有两个实现类的bean,这时候它不知道注入哪一个,就会报错,
//使用了List集合的话,就会把I的两个实现类的bean都注入到List集合中
System.out.println(i);
//没有使用@Order注解时的打印结果为:[com.lt.order.A@4760f169, com.lt.order.B@261ea657]
//这时候是A在前面,B在后面
}
}
在测试类中,用@Autowired注入 I 的bean时,如果不用List集合的话就只能获取一个 I 的bean,但是 I 是有两个实现类的bean,这时候它不知道注入哪一个,就会报错,使用了List集合的话,就会把 I 的两个实现类的bean都注入到List集合中,并且这些bean在List集合中是有顺序的,如果这时候我们想哪个bean排在前面(先执行),就可以通过@Order去改变他的自动装配顺序,谁@Order里面的值小谁排在前面
示例:
用法一,直接加注解,@Order注解中的属性值越小就排的越前
@SpringBootTest(classes = TestOrder.class)
public class TestOrder {
@Bean
@Order(1)
public A a(){
return new A();
}
@Bean
@Order(0) // 谁小谁在前面
public B b(){
return new B();
}
@Test
public void test(@Autowired List<I> i){
System.out.println(i);
//使用@Order注解后的打印结果为:[com.lt.order.B@261ea657, com.lt.order.A@4760f169]
//这时候变成了B在前面,A在后面
}
}
用法二,实现Ordered接口,重写接口中的getOrder()方法,这个方法的返回值越小,实现类作为bean在List数组中就排的越前面。这下面代码的结果和上面直接加注解的结果一样
public class A implements I, Ordered {
public A(){
System.out.println("A");
}
@Override
public int getOrder() {
return 1;
}
}
public class B implements I, Ordered {
public B(){
System.out.println("B");
}
@Override
public int getOrder() {
return 0;
}
}
2、@DependsOn 改变Bean的创建顺序
@DependsOn注解可以定义在类和方法上,意思是我这个组件要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中。
使用场景:
比如我现在有A类和B类,A类是数据库查询,B类是数据库连接,在javaconfig(配置类)中使用@Bean注解把这两个类配置成bean时,可以通过把B类配置成bean的语句写在A类配置成bean的语句前面,来实现先连接数据再查询数据库,但如果配置bean的方式用的是@Component及其衍生注解时,就不好控制bean的创建顺序了,这时候就可以用@DependsOn来改变Bean的创建顺序。
需要用到观察者模式的情况下通常都需要用到该注解,观察者模式(详细可查看相关文章)有三要输,观察者、事件源、事件,机制是观察者会监听数据源的某些时间,当事件源触发该事件后,观察者就会知道进行相应措施。
比如老师是观察者,学生是事件源,学生迟到是事件,老师观察学生是否迟到,每当学生迟到,老师就会发现,并处罚该学生。
这类场景一般需要观察者要比事件源先创建,才能不遗漏事件源触发的每一个事件,要是事件源先创建,可能会在观察者创建前就触发了事件而观察者无法知道。
比如上述例子,八点算迟到,但是老师自己都八点半才到学校,所以就无法知道八点到八点半之间迟到的学生。
@DependsOn注解可以作用在方法和类上。
(1)当@DependsOn注解作用在类上时
当作用在类上时,通常会与@Component及其衍生注解等注解配合使用。
示例:
代码:还没使用@DependsOn注解
//事件源EventSource,在com.lt.dependsOn包下
@Component
public class EventSource {
public EventSource(){
//当调用这个无参构造函数创造bean时,就会执行这条打印语句
System.out.println("事件源创建");
}
}
//监听类EventTListener,在com.lt.dependsOn包下
@Component
public class EventTListener {
public EventTListener(){
//当调用这个无参构造函数创造bean时,就会执行这条打印语句
System.out.println("监听器创建");
}
}
//测试类TestDepends,在com.lt.dependsOn包下
@ComponentScan("com.lt.dependsOn") //这里要指定扫描"com.lt.dependsOn"这个包,因为这时候用的是@Component来配置bean
@SpringBootTest(classes = TestDepends.class)
public class TestDepends {
@Test
public void test(){
}
}
还没使用@DependsOn注解的运行结果
因为spring默认扫描包时会根据文件在文件夹的位置先后顺序扫描加载,而EventSource 文件位置在EventTListener前面,所以会先加载EventSource 事件源组件。但这就使得两个类的依赖关系不符合逻辑。
使用@DependsOn注解:
//事件源
@Component
@DependsOn(value = {"eventTListener"})
public class EventSource {
public EventSource(){
//当调用这个无参构造函数创造bean时,就会执行这条打印语句
System.out.println("事件源创建");
}
}
使用@DependsOn注解的运行结果:
这时候就是监听器先创建了。注意:@DependsOn中value属性的bean id必须存在,不然会报错。
(2)当@DependsOn注解作用在方法上时
当作用在方法上时,通常会与@Bean注解配合使用。
要把EventSource和EventTListener类上的注解去掉,再使用下面的代码,因为下面是使用@Bean注解来配置bean的方式
测试类TestDepends代码:
//测试类
@SpringBootTest(classes = TestDepends.class)
public class TestDepends {
@Bean
@DependsOn(value = {"eventListener"})
public EventSource eventSource(){
return new EventSource();
}
@Bean
public EventTListener eventListener(){
return new EventTListener();
}
@Test
public void test(){
}
}
如果不加@DependsOn注解的话,就会先创建事件源,如果加了(如上代码),就会先创建监听器(如下图)
推荐:
【Spring】依赖注入(DI)时常用的注解@Autowired和@Value-CSDN博客https://blog.csdn.net/m0_65277261/article/details/137784706?spm=1001.2014.3001.5501
【Java网络编程】TCP通信(Socket 与 ServerSocket)和UDP通信的三种数据传输方式-CSDN博客https://blog.csdn.net/m0_65277261/article/details/137926277?spm=1001.2014.3001.5501【Spring】使用@Bean和@Import注解配置Bean,与Bean的实例化_import和bean-CSDN博客https://blog.csdn.net/m0_65277261/article/details/137257177?spm=1001.2014.3001.5501