Spring之@DependsOn的實現原理

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章