为什么要控制Bean的加载顺序?
首先spring容器对一般的bean的初始化顺序是不确定的(个别Spring自身初始化用的bean和配置类的bean会优先初始化),但是我们在某些场景下(具体场景如下面举例),我们又需要控制顺序。这时候,就用到了@DenpendsOn。
一、@DependsOn的使用
使用场景:
1、beanA 间接依赖 beanB并不是直接通过 构造方法或@Autowired等方式注入。如果beanA有一个属性,需要在初始化的时候对其进行赋值(需要在初始化的时候做,是因为这个属性其实是包装了其它的几个Bean的,比如说代理了BeanB,所以这就形成了BeanA间接的依赖BeanB。
2、beanA是事件发布者(或JMS发布者),beanB(或其他监听器)负责监听这些事件,典型的如观察者模式。我们不想监听器beanB错过任何事件,那么B需要首先被初始化。
代码示例:
@ComponentScan("com")
public class AppConfig {
}
@Component
public class EventA {
public EventA() {
System.out.println("EventA:初始化");
}
}
@Component
public class EventB {
public EventB() {
System.out.println("EventB:初始化");
}
}
public class DependsOnTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
运行结果:
AMonitor:初始化
EventA:初始化
EventB:初始化
按照上面的运行结果来看,监听器初始化时候,事件A和事件B还没有加载完成。这样就会导致我们错过事件,这在开发中是绝对不允许的。那怎么办?这时候就需要我们的**@DependsOn**注解了。
我们在我们的AMonitor监听器上加上注解,其他代码不变,代码如下。
@Component
@DependsOn({"eventA","eventB"})
public class AMonitor {
public AMonitor() {
System.out.println("AMonitor:初始化");
}
}
再次查看运行结果:
EventA:初始化
EventB:初始化
AMonitor:初始化
运行结果,如我们预期。简单的使用就介绍到这里。
二、@DenpendsOn原理介绍
我们都知道在使用一个bean之前,肯定会去创建一个bean。spring中常见的套路就是,每次都是先判断当前这个组件有没有,没有就去创建,有就直接拿来用,包括第一次使用也是。
照这样说的话,那么我们在使用AMonitor监听器这个bean之前,肯定先去判断有没有,因为我们是初始化,肯定是没有的,所以去创建。
在spring容器初始化过程中,使用到bean 首先要去,getBean(非关键代码直接省略,有兴趣的可以打断点看调用栈)。
然后调用AbstractBeanFactory类中的doGetBean()方法。
doGetBean()代码如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 先去单例池中判断,有没有当前这个bean。
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
//如果单例池中没有这个bean往下走
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 从合并后的beandefinition中,拿到@DepensOn注解内容,和注解的值一样是个数组。
String[] dependsOn = mbd.getDependsOn();
//判断dependsOn这个数组是否为空
if (dependsOn != null) {
for (String dep : dependsOn) {
//互相依赖,直接抛出异常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
//这个dependentBeanMap是在将一个.class编程beandefinition时,获取注解@DependsOn注解的内容,然后填入的。为的就是以后创建bean的时候使用。
//如果判断都通过,再进入这个方法,验证dependentBeanMap中是否有被依赖的bean。
registerDependentBean(dep, beanName);
try {
//验证通过,直接去getBean(),然后循环以上步骤,调用doGetBean,然后调用createBean先把被依赖的bean创建出来(EventA,EventB),再往下走,继续创建当前bean的过程(AMonitor)。
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
registerDependentBean()方法:
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
/**
这个dependentBeanMap是在将一个.class编程beandefinition时,
获取注解@DependsOn注解的内容,然后填入的。为的就是以后创建bean的时候使用。
**/
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
//然后把dependentBeanName=AMonitor,添加到dependentBeans。
//作为缓存,后续使用的时候直接拿。
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
三、总结
其实我们通过看源码以后,可以大体认为@DependsOn实际上就是通过,提前做判断,然后提前去创建了被依赖的bean。当然,真正源码中的处理,还要考虑到很多情况,例如bean是否是单例,bean是否是FactoryBean等等,而且你要真正的理解源码,还要去搞懂spring在这之前做了什么,这是一个漫长的学习过程。博文中只是做了简单的介绍,还有很多不足的地方,欢迎大家的友善建议,如果有对spring有深厚的兴趣,也可以加入Java技术交流群:805069260,欢迎大家。
来源:CSDN
作者:Evan″
链接:https://blog.csdn.net/qq_41378597/article/details/103688671