最簡 Spring IOC 容器源碼分析

前言

許多文章都是分析的 xml 配置,可是如今 Spring Boot 開發多基於註解。本文從註解的角度分析 Spring IOC 容器源碼。java

版本:git

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

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

BeanDefinition

BeanDefinition 接口定義了一個包含屬性、構造器參數、其餘具體信息的 bean 實例。web

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,這些註解有:spring

  • 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) 方法調用。session

@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 在程序運行時是不可更改的,可是實現類能夠從新再入配置信息。app

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!

相關文章
相關標籤/搜索