1. 程式人生 > 實用技巧 >最簡 Spring IOC 容器原始碼分析

最簡 Spring IOC 容器原始碼分析

前言

許多文章都是分析的 xml 配置,但是現在 Spring Boot 開發多基於註解。本文從註解的角度分析 Spring IOC 容器原始碼。

版本:

  • Spring Boot:2.1.6.RELEASE
  • Spring FrameWork:5.1.8.RELEASE
  • Java 8

文章部分內容參考自:https://www.javadoop.com/post/spring-ioc

BeanDefinition

BeanDefinition 介面定義了一個包含屬性、構造器引數、其他具體資訊的 bean 例項。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	// ConfigurableBeanFactory 中只有 2 種:singleton 和 prototype。
	// request, session 等是基於 Web 的擴充套件
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
	
	// 不重要
	int ROLE_APPLICATION = 0;
	int ROLE_SUPPORT = 1;
	int ROLE_INFRASTRUCTURE = 2;


	// 設定父 Bean 的資訊(繼承父 Bean 的配置資訊)
	void setParentName(@Nullable String parentName);
	
	@Nullable
	String getParentName();

	// 設定 Bean 的類名稱,要通過反射來生成例項
	void setBeanClassName(@Nullable String beanClassName);

	// 返回當前 Bean 的 class name
	String getBeanClassName();


	void setScope(@Nullable String scope);

	@Nullable
	String getScope();

	// 是否延遲初始化
	void setLazyInit(boolean lazyInit);

	boolean isLazyInit();

	// 設定該 Bean 依賴的所有的 Bean,並非 @Autowire 標記的
	void setDependsOn(@Nullable String... dependsOn);

	@Nullable
	String[] getDependsOn();

	// 設定該 Bean 是否可以注入到其他 Bean 中,只對根據型別注入有效,
   // 如果根據名稱注入,即使這邊設定了 false,也是可以的
	void setAutowireCandidate(boolean autowireCandidate);

	boolean isAutowireCandidate();

	// 同一介面的多個實現,如果不指定名字,Spring 會優先選擇設定 primary 為 true 的 bean
	void setPrimary(boolean primary);

	boolean isPrimary();

	// 如果該 Bean 採用工廠方法生成,指定工廠名稱;否則用反射生成
	void setFactoryBeanName(@Nullable String factoryBeanName);

	@Nullable
	String getFactoryBeanName();

	// 指定工廠類中的 工廠方法名稱
	void setFactoryMethodName(@Nullable String factoryMethodName);

	@Nullable
	String getFactoryMethodName();

	// 返回該 bean 的構造器引數
	ConstructorArgumentValues getConstructorArgumentValues();

	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	// Bean 中的屬性值,返回的例項在 bean factory post-processing 期間會被更改
	MutablePropertyValues getPropertyValues();

	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	void setInitMethodName(@Nullable String initMethodName);

	@Nullable
	String getInitMethodName();

	void setDestroyMethodName(@Nullable String destroyMethodName);

	@Nullable
	String getDestroyMethodName();

	
	void setRole(int role);
	int getRole();
	void setDescription(@Nullable String description);
	@Nullable
	String getDescription();

	// Read-only attributes
	boolean isSingleton();
	boolean isPrototype();
	boolean isAbstract();
	@Nullable
	String getResourceDescription();
	@Nullable
	BeanDefinition getOriginatingBeanDefinition();
}

AnnotationConfigUtils#processCommonDefinitionAnnotations(...)

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
	AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
	if (lazy != null) {
		abd.setLazyInit(lazy.getBoolean("value"));
	}
	else if (abd.getMetadata() != metadata) {
		lazy = attributesFor(abd.getMetadata(), Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
	}

	if (metadata.isAnnotated(Primary.class.getName())) {
		abd.setPrimary(true);
	}
	AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
	if (dependsOn != null) {
		abd.setDependsOn(dependsOn.getStringArray("value"));
	}

	AnnotationAttributes role = attributesFor(metadata, Role.class);
	if (role != null) {
		abd.setRole(role.getNumber("value").intValue());
	}
	AnnotationAttributes description = attributesFor(metadata, Description.class);
	if (description != null) {
		abd.setDescription(description.getString("value"));
	}
}

可以看到,processCommonDefinitionAnnotations 方法會根據註解來填充 AnnotatedBeanDefinition,這些註解有:

  • Lazy
  • Primary
  • DependsOn
  • Role
  • Description

向上檢視呼叫,發現會在 ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass 將其註冊為一個 bean definition。

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
	AnnotationMetadata metadata = configClass.getMetadata();
	AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

	ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
	configBeanDef.setScope(scopeMetadata.getScopeName());
	String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
	// 1. 通過註解填充 configBeanDef
	AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	// 2. 將 bean definition 註冊到 registry 中
	this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
	configClass.setBeanName(configBeanName);

	if (logger.isTraceEnabled()) {
		logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
	}
}

最終會被 AbstractApplicationContext#refresh 的 invokeBeanFactoryPostProcessors(beanFactory) 方法呼叫。

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			initMessageSource();

			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			onRefresh();

			// Check for listener beans and register them.
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			finishRefresh();
		}

		...
	}
}

BeanFactory 簡介

BeanFactory 是生產 bean 的工廠,它負責生產和管理各個 bean 例項。從下圖可以看到,ApplicationContext 也是一個 BeanFactory。如果說 BeanFactory 是 Spring 的心臟,那麼 ApplicationContext 就是完整的身軀。

ApplicationContext 是應用程式執行時提供配置資訊的通用介面。ApplicationContext 在程式執行時是不可更改的,但是實現類可以重新再入配置資訊。

ApplicationContext 的實現類有很多,比如 AnnotationConfigApplicationContext, AnnotationConfigWebApplicationContext, ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, XmlWebApplicationContext 等。我們上面分析的就是 AnnotationConfigApplicationContext,其採用註解的方式提供配置資訊,這樣我們就不用寫 XML 配置檔案了,非常簡潔。

Web 容器啟動過程

本文使用 Spring Boot 開發,其啟動的程式碼是:

@SpringBootApplication
@EnableScheduling
@EnableAspectJAutoProxy
public class AppApplication {

    public static void main(String[] args) {
        SpringApplication.run(AppApplication.class, args);
    }

}

核心的點是這一句:

SpringApplication.run(AppApplication.class, args);

SpringApplication 的程式碼就不分析了,明確本次看原始碼的目的是分析容器原始碼,Spring Boot 的啟動過程和其他資訊都忽略了,因為 Spring 程式碼實在是龐雜。分析上面的 run 方法,最終會追蹤到 SpringApplication#run(...) 方法。

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

跟 context 相關的,是下面這 3 句程式碼:

prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);

refreshContext 方法就是重新整理給定的 context:

private void refreshContext(ConfigurableApplicationContext context) {
	refresh(context);
	if (this.registerShutdownHook) {
		try {
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
}
protected void refresh(ApplicationContext applicationContext) {
	Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
	((AbstractApplicationContext) applicationContext).refresh();
}

會發現最終呼叫到了 AbstractApplicationContext#refresh 方法。註釋參考自:https://www.javadoop.com/post/spring-ioc

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 來個鎖,不然 refresh() 還沒結束,你又來個啟動或銷燬容器的操作,那不就亂套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 準備工作,記錄下容器的啟動時間、標記“已啟動”狀態、處理配置檔案中的佔位符
      prepareRefresh();

      // 這步比較關鍵,這步完成後,配置檔案就會解析成一個個 Bean 定義,註冊到 BeanFactory 中,
      // 當然,這裡說的 Bean 還沒有初始化,只是配置資訊都提取出來了,
      // 註冊也只是將這些資訊都儲存到了註冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設定 BeanFactory 的類載入器,新增幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
      prepareBeanFactory(beanFactory);

      try {
         // 【這裡需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現了此介面,
         // 那麼在容器初始化以後,Spring 會負責呼叫裡面的 postProcessBeanFactory 方法。】

         // 這裡是提供給子類的擴充套件點,到這裡的時候,所有的 Bean 都載入、註冊完成了,但是都還沒有初始化
         // 具體的子類可以在這步的時候新增一些特殊的 BeanFactoryPostProcessor 的實現類或做點什麼事
         postProcessBeanFactory(beanFactory);
         // 呼叫 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 註冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
         // 此介面兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個方法分別在 Bean 初始化之前和初始化之後得到執行。注意,到這裡 Bean 還沒初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化當前 ApplicationContext 的 MessageSource,國際化這裡就不展開說了,不然沒完沒了了
         initMessageSource();

         // 初始化當前 ApplicationContext 的事件廣播器,這裡也不展開了
         initApplicationEventMulticaster();

         // 從方法名就可以知道,典型的模板方法(鉤子方法),
         // 具體的子類可以在這裡初始化一些特殊的 Bean(在初始化 singleton beans 之前)
         onRefresh();

         // 註冊事件監聽器,監聽器需要實現 ApplicationListener 介面。這也不是我們的重點,過
         registerListeners();

         // 重點,重點,重點
         // 初始化所有的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最後,廣播事件,ApplicationContext 初始化完成
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         // 銷燬已經初始化的 singleton 的 Beans,以免有些 bean 會一直佔用資源
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

核心流程就是 try 程式碼塊裡的內容,我們應該瞭解整體原理,本篇文章並不能逐行逐句分析。如果那樣做,完全就變成一部字典了……

bean 的載入

bean 載入的呼叫函式:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
		@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

	// 提取對應 bean 的名字
	final String beanName = transformedBeanName(name);
	Object bean;

	// 1. 重要,重要,重要!
	// 建立單例 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);
	}

	else {
		// 存在迴圈依賴
		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);
			}
		}

		// 如果不是僅僅做型別檢查,則是建立 bean,需要做記錄
		if (!typeCheckOnly) {
			markBeanAsCreated(beanName);
		}

		try {
			// 獲取 RootBeanDefinition,如果指定 beanName 是子 bean 的話,需要合併父類屬性
			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);

			// 若存在依賴,需要遞迴例項化依賴的 bean
			String[] dependsOn = mbd.getDependsOn();
			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 + "'");
					}
					registerDependentBean(dep, beanName);
					try {
						getBean(dep);
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
					}
				}
			}

			// 建立 bean 例項
			
			// Singleton 模式的建立
			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);
			}

			// Prototype 模式的建立
			else if (mbd.isPrototype()) {
				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;
		}
	}

	// 檢測 requiredType 是否為 bean 的實際型別,不是則轉換,不成功則丟擲異常
	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;
}

可以看到 bean 的載入是相當複雜的。載入的步驟大致如下:

  1. 轉換對應 beanName
  2. 嘗試從快取中載入單例
  3. bean 的例項化
  4. 原型模式的依賴檢查
  5. 檢測 parentBeanFactory
  6. 將配置檔案轉換為 RootBeanDefinition
  7. 尋找依賴
  8. 針對不同的 scope 進行 bean 的建立
  9. 型別轉換

FactoryBean

前面提到了 BeanFactory,這裡又來了個 FactoryBean …… 據說 Spring 提供了 70 多個 FactoryBean 的實現,可見其在 Spring 框架中的地位。它們隱藏了例項化複雜 bean 的細節,給上層應用帶來便捷。

public interface FactoryBean<T> {

	// 返回 FactoryBean 建立的 bean 例項,如果 isSingleton() 返回 true,則該例項會放到 Spring 容器的單例快取池中
	@Nullable
	T getObject() throws Exception;

	// 返回 FactoryBean 建立的 bean 型別
	@Nullable
	Class<?> getObjectType();

	default boolean isSingleton() {
		return true;
	}
}

迴圈依賴

迴圈依賴就是迴圈引用,兩個或多個 bean 相互之間持有對方。那麼 Spring 是如何解決迴圈依賴的?

在 Spring 中迴圈依賴一共有 3 種情況:

  1. 構造器迴圈依賴
  2. setter 迴圈依賴
  3. prototype 範圍的依賴處理

其中構造器迴圈依賴是無法解決的,因為一個 bean 建立時首先要經過構造器,但是構造器相互依賴,就相當於 Java 中多執行緒死鎖。

setter 注入造成的依賴是通過 Spring 容器提前暴露剛完成構造器注入但未完成其他步驟(如 setter 注入)的 bean 來完成的,而且只能解決單例作用域的 bean 迴圈依賴。通過提前暴露一個單例工廠方法,從而使其他 bean 能引用到該 bean,程式碼如下:

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

其中 earlySingletonObjects 的定義為:

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

對於 prototype 作用域的 bean,Spring 容器無法完成依賴注入,因為 Spring 容器不快取 prototype 作用域的 bean。

bean 生命週期

面試的話,Spring 的核心就在這裡了,不過只要記住大體流程就行。

公眾號

coding 筆記、點滴記錄,以後的文章也會同步到公眾號(Coding Insight)中,希望大家關注_

程式碼和思維導圖在 GitHub 專案中,歡迎大家 star!