7. spring源码篇之DependOn

简介

很多时候一个Bean需要依赖另一个Bean先实例化,例如一个BBean实例化设置了一个全局属性,而ABean恰好需要依赖于这个属性,没有就报错,但是这个顺序自己是把握不了的,这个时候就可以使用DependOn

示例

public class GlobalContext {
    public static boolean isActive = false;
}


@Component
public class BBean {
    public BBean() {
        GlobalContext.isActive = true;
    }
}

@Component
public class ABean {
    public ABean() throws IllegalAccessException {
        if (!GlobalContext.isActive) {
            throw new IllegalAccessException("执行异常");
        }
    }
}

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    context.getBean("ABean");
}

运行上面代码,报错

Caused by: java.lang.IllegalAccessException: 执行异常

将ABean加上注解@DependsOn({“BBean”})

@Component
@DependsOn({"BBean"})
public class ABean {
    public ABean() throws IllegalAccessException {
        if (!GlobalContext.isActive) {
            throw new IllegalAccessException("执行异常");
        }
    }
}

正常运行

源码分析

// 查看当前Bean是否需要依赖于其它bean dependsOn属性
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
    for (String dep : dependsOn) {
        // 检查当前Bean是不是又被dep所依赖了,这样的话那么就出现了循环以来,这种事无法解决的,只能抛出异常
        if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
        }
        // 保存依赖关系
        registerDependentBean(dep, beanName);

        try {
            // 先实例化依赖的Bean
            getBean(dep);
        } catch (NoSuchBeanDefinitionException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
        }
    }
}

上面源码逻辑也比较清晰,就是如果依赖于另一个Bean,那么就先实例化另一个Bean

那么如果a依赖于b,b又依赖于a,那么就会出现循环依赖,那么是如何发现的呢,逻辑就在isDependent方法里面

// 保存某个Bean依赖了哪些Bean
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

// 保存某个Bean被哪些bean依赖了
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
    
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
   
    String canonicalName = canonicalName(beanName);
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    if (dependentBeans == null) {
        return false;
    }
    
    // 表示出现循环依赖
    if (dependentBeans.contains(dependentBeanName)) {
        return true;
    }
    
    // a->b->c->a 循环判断
    for (String transitiveDependency : dependentBeans) {
        if (alreadySeen == null) {
            alreadySeen = new HashSet<>();
        }
        alreadySeen.add(beanName);
        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
            return true;
        }
    }
    return false;
}

以上就是DependOn的源码,就是通过两个Map保存了依赖关系,从而发现循环依赖


欢迎关注,学习不迷路!

你可能感兴趣的:(spring,framework,spring,java)