spring容器载入bean顺序是不确定的,spring框架没有约定特定顺序逻辑规范。但spring保证如果A依赖B(如beanA中有@Autowired B的变量),那么B将先于A被加载。但如果beanA不直接依赖B,我们如何让B仍先加载呢?
需要的场景距离如下
- bean A
间接(并不是直接@Autowired)
依赖 bean B。如bean A有一个属性,需要在初始化的时候对其进行赋值(需要在初始化的时候做,是因为这个属性其实是包装了其它的几个Bean的,比如说代理了Bean B),所以这就形成了Bean A间接的依赖Bean B了 - bean A是事件发布者(或JMS发布者),bean B (或一些) 负责监听这些事件,典型的如观察者模式。我们不想B 错过任何事件,那么B需要首先被初始化。
以上是两种典型的,Bean初始化的时候存在依赖关系的情况,都可以通过@DependsOn
来解决 。
例子
准备工作:(两个controller和一个service)
@Controller
public class HelloController {
public HelloController() {
System.out.println("HelloController 初始化。。。");
}
@ResponseBody
@GetMapping("/hello")
public String helloGet() throws Exception {
return "hello...Get";
}
}
@Controller
public class AsyncHelloController {
public AsyncHelloController() {
System.out.println("AsyncHelloController 初始化。。。");
}
}
@Service
public class HelloServiceImpl implements HelloService {
public HelloServiceImpl() {
System.out.println("HelloServiceImpl 初始化。。。");
}
}
启动容器,打印顺序(初始化顺序如下:)
HelloServiceImpl 初始化。。。
AsyncHelloController 初始化。。。
HelloController 初始化。。。
需要注意的是:这个demo的日志都是放在默认的构造函数里面的,因此即使你使用了@Autowired
,也是不会打乱构造函数的执行顺序的,因为,因为@Autowired的解析发生在给属性赋值的populate()方法里,这个时候自己已经实例化了,才会去给属性赋值, 所以如果你要求的时机稍微比较晚可以在赋值期间、或者实例化期间。
@DependsOn:让HelloController在AsyncHelloController之前实例化
//@DependsOn // 这里面写String数组。不写不会生效,但是若写了,名字要写正确,否则会报错的
@DependsOn({"helloController"}) // 名称必须写对,必须是容器里存在的Bean,否则启动报错的(fast-fail是好事)
@Controller
public class AsyncHelloController {
...
}
HelloServiceImpl 初始化。。。
HelloController 初始化。。。 --> HelloController先被实例化了~~~
AsyncHelloController 初始化。。。
使用@Lazy间接实现
@Lazy
public class AsyncHelloController {
...
}
HelloServiceImpl 初始化。。。
HelloController 初始化。。。
我们发现它只有两句输出,这个时候AsyncHelloController
还没有实例化。只有首次访问它的时候才会实例化,所以我们是通过间接的方式
实现了这个效果。