爲什麼要控制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,歡迎大家。