Spring的IOC淺析

上篇寫了Spring的AOP,文章鏈接Spring的AOP淺析
這一次,我們聊聊Spring的另一大核心功能,IOC。
IOC翻譯成中文,意思是:控制反轉。我們從字面意思上分析一下這個詞語。
1.控制
看下面的例子:A類依賴B類。

public class A {
    public void func(){
		B b = new B();
		b.func();
	}
}

如上:我們在A中想要使用B的時候,A需要自己去new一個B出來。此時,A就和B強耦合在一起,B有變化,A就要發生變化。這明顯不符合程序設計的開閉原則。
有了IOC後,A需要B的時候,怎麼辦呢?我們可以直接將B進行注入。方式如下:

public class A {
	@Autowired
	private B b;
    public void func(){
		b = new B();
		b.func();
	}
}

此時,B依賴就是由IOC容器負責實例化,然後注入到A中。
控制講的就是:一個類如何獲取它所需要的依賴。從自己new一個B到由IOC根據需要注入B。此時,對B的控制就發生了反轉。
維基百科是這樣定義“控制反轉”的。
2004年,Martin Flower提出了 "哪些方面的控制被反轉了?"這個問題,他得出的結論是:依賴對象的獲得被反轉了。關於這個結論,他爲控制反轉起了一個很好聽的名字“依賴注入”。許多非凡的應用都是由兩個或者多個類通過彼此的合作來實現業務邏輯的,這使得每個對象都需要與其合作的對象(也就是它所依賴的對象)的引用。如果這個獲取過程要考自身實現,那麼如你所見,這將導致代碼高度耦合並且難以測試。
在有IOC之前,企業級POJO的王者是EJB。我曾經維護過一個EJB的項目,實現和Spring IOC相同的功能,要寫一大堆接口。並且EJB還需要依賴實現了EJB協議的容器,比如:JBoss或者weblogic。運維難度比tomcat要高好多。所以,相比之下,IOC現在非常的流行。
說回IOC,上面我們聊了一下IOC能做的事。接下來,我們就得聊聊IOC是如何實現的?
從源碼角度介紹一下IOC的結構體系
在這裏插入圖片描述
在實際使用中,BeanFactory和Application是兩條線。其中:
BeanFactory<-----HierarchicalBeanFactory<------ConfigurableBeanFactory
這是一條線,代表了簡單IOC容器,提供最基本的IOC功能。如:getBean。
ResourceLoader<-------ApplicationContext<------WebApplicationContext
這是一條線,代表了高級容器,除了繼承了父接口的getBean功能,又通過繼承MessageSource、ApplicationEventPublisher、ResourceLoader接口獲得了高級功能。
我們以FileSystemXmlApplicationContext爲例來說明一下IOC容器的啓動過程。
該類的體系結構大致如下:
在這裏插入圖片描述
下面我們說一下IOC容器的啓動以及Bean對象的生成過程。啓動和生成是兩個過程。IOC容器的啓動是伴隨着Web工程開始的,但是Bean對象的生成一般是第一次調用getBean對象時生成。
我們使用一下FileSystemXmlApplicationContext這個IOC容器。

@Test
    public void testFileSystem(){
        FileSystemXmlApplicationContext file = new FileSystemXmlApplicationContext("classpath:config/applicationContext.xml");
        file.getBean("testBean");
    }

這個IOC容器的啓動入口在構造器中的refresh方法

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if(refresh) {
            this.refresh();
        }
    }

進入構造器,我們看到裏面有一個setConfigLocations方法。這個方法就是設置配置文件的位置,之後調用refresh方法啓動IOC容器。
我們深入到refresh方法內部看一下,IOC容器啓動的具體流程
在這裏插入圖片描述
上圖中,我標示出來的方法,obtainFreshBeanFactory()。進去看一下。
最終加載xml文件中bean定義的方法在AbstractRefreshableApplicationContext類的refreshBeanFactory方法。
在這裏插入圖片描述
先是createBeanFactory,創建出一個DefaultListableBeanFactory,大家記住這個類。這個類是已經具備了IOC容器的基本功能,我們如果要擴展自己的IOC容器,可以從這個類下手。像XmlBeanFacotory或者ApplicationContext的實現,都是對這個類進行擴展。
我們繼續往下看。
loadBeanDefinitions(beanFactory)方法開始讀取Spring的配置文件。
接下來,我們進入AbstractXmlApplicationContext類,看一下loadBeanDefinitions(beanFactory)的方法實現。
在這裏插入圖片描述
首先先創建出一個XmlBeanDefinitionReader對象,這個對象是真正讀取xml文件的類。然後繼續看loadBeanDefinitions(beanDefinitionReader)裏面的實現,最終流程來到了XmlBeanDefinitionReader對象的loadBeanDefinitions(EncodedResource encodedResource)方法。根據Spring配置文件資源對象初始化了一個InputStream流。之後根據InputStream流構建Document對象。然後就開始解析這個Document對象。
解析Document對象,是在DefaultBeanDefinitionDocumentReader類的parseBeanDefinitions方法中。
在這裏插入圖片描述
我們進入parseDefaultElement方法中,可以看到我們熟悉的標籤定義了
在這裏插入圖片描述
比如:import、alias、bean、beans。我們以bean標籤爲例,看一下Spring IOC是如何對bean標籤進行解析的。進入processBeanDefinition方法中。
首先,我們看到一個BeanDefinitionHolder對象,這個對象很重要,它封裝了Bean對象的一系列描述性信息,比如:bean的class名稱,bean的名稱、bean是否是lazy-init類型,bean的依賴注入模式,是否開啓了bean依賴的檢查等等。
並且BeanDefinitionHolder持有Spring BeanDefinition對象,BeanDefinition是Spring 的Bean對象表示,是IOC中的Bean的基礎數據機構,後期真正的Bean對象會依據該數據結構生成。但此時,這個對象還不能使用。因爲,Bean中的依賴注入還沒有發生。目前,它只是定義好了,將來要生成的Bean是一個什麼樣子。真正開始創建Bean的時候是在第一次獲取Bean對象的時候,也就是getBean方法的第一次調用。
當然,凡事無絕對。Spring在這個地方增加了一個開關,就是lazy-init屬性。如果我們顯示的將lazy-init設置爲false。此時,在BeanDefinition初始化階段。就會將該類需要的依賴進行注入,此時,在IOC容器啓動的過程中,就會生成真正意義上的Bean對象。
lazy-init屬性的處理是在refresh方法的finishBeanFactoryInitialization方法中。感興趣的朋友可以去看一下。
我們繼續看registerBeanDefinition方法,這個方法會將BeanDefinition對象注入到IOC容器中。進入DefaultListableBeanFactory類的registerBeanDefinition方法。
在這裏插入圖片描述
我們看到一個名稱爲beanDefinitionMap的ConcurrentHashMap對象,這個Map對象就是IOC容器。
到目前爲止,我們已經看到了IOC容器的真正容貌。其實也沒有啥,對吧?哈哈。開玩笑,通過上面的一系列分析,我們看到Spring的體系非常的龐大,其中很多設計,非常值得我們學習。
好了,繼續看registerBeanDefinition方法的實現。
首先,會校驗bean的名稱是否爲空。之後開始對beanDefinition進行校驗。然後會根據bean的名稱從beanDefinitionMap判斷是否已經創建了這個BeanDefinition對象。
如果這個對象不爲空,會檢查是否設置allowBeanDefinitionOverriding屬性。默認值是true。
之後,會將BeanDefinition放進beanDefinitionMap對象。至此,我們已經完成了Spring配置文件的解析,註冊。此時,IOC容器已經準備完畢。等待着getBean方法調用。
下面,我們看一下getBean方法的實現。
繼續用我們上面的那個例子。
getBean方法,實際就是將我們上面的beanDefinition對象根據bean的id從beanDefinitionMap中取出來,然後根據beanDefinition對象的定義生成真正的bean對象。
下面,我們看一下具體的實現。
點擊getBean方法,首先進入AbstractApplicationContext類中的getBean方法。從這裏也可以看出,IOC容器對於一些比較基礎的功能,是由父類完成。子類在父類功能的基礎上擴展高級功能。
在這裏插入圖片描述
getBean方法裏首先獲取一下BeanFactory對象,這個beanFactory對象是我們在FileSystemXmlApplicationContext的refresh方法中初始化進去的,是DefaultListableBeanFactory。接着執行getBean(String beanName)方法。方法入參是一個String類型。這個getBean(String beanName)方法是在DefaultListableBeanFactory的父類AbstractBeanFactory類中。
在這裏插入圖片描述
首先我們看到getObjectForBeanInstance方法,該方法是處理FactoryBean這種特殊的bena。如果我們的bean對象需要從FactoryBean中獲取,走的就是getObjectForBeanInstance方法。我們可以進去看一下,從FactoryBean中獲取bean對象的核心代碼在FactoryBeanRegistrySupport類的doGetObjectFromFactoryBean方法中。在其中,我們可以看到我們熟悉的factory.getObject方法,在上一篇AOP中我們講過,AOP的核心對象是ProxyFactoryBean。這個bean負責對target類進行增強,之後產生一個代理對象。當時,我們就提到了getObject方法,這個方法是獲取代理對象的入口。忘記的朋友可以再去回顧一下Spring的AOP解析
我們回到AbstractBeanFactory類的doGetBean方法中,繼續往下看。
根據beanName先從父容器中查找beanDefinition對象。找到直接返回,找不到,從子容器中查找。
子容器中查找的代碼是:getMergedLocalBeanDefinition(beanName)方法。
最終獲取beanDefinition對象的地方在DefaultListableListableBeanFactory類的getBeanDefintion方法,如下圖:
在這裏插入圖片描述
是不是很眼熟?沒錯,我們在IOC容器啓動時,把id和BeanDefinition的映射關係存儲進了beanDefinitionMap中。現在,開始真正的使用bean對象了,我們從這個map中再通過id把BeanDefinition對象拿出來。
如果找不到,就直接報出bean找不到異常
拿到BeanDefiniton對象後,下面開始真正的創建bean。首先,找到當前bean的依賴對象有哪些,然後從IOC容器中找到這些對象的定義。
之後開始創建bean,這裏需要判斷單例bean還是多例bean。我們以單例bean對象爲例:
看一下createBean(beanName,mbd,args)方法,如下:
在這裏插入圖片描述
這個方法裏,首先是處理BeanDefinition對象。填充字節碼信息等。之後,調用doCreateBean(beanName, mbdToUse, args)方法。我們進入該方法。該方法中,首先根據class生成bean對象。
在這裏插入圖片描述
生成bean對象的方式有三種。
1).根據工廠方法生成bean對象

if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

2).根據構造函數生成bean對象

// Candidate constructors for autowiring?
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

3).根據默認構造函數生成bean對象

// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);

bean對象有了,下面開始對bean對象進行依賴注入以及執行bean的生命週期相關的一系列方法

// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//依賴注入
			populateBean(beanName, mbd, instanceWrapper);
			//實例化bean
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}

我們先看依賴注入的實現
可以看到依賴注入有兩種方式。

int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		//根據名稱注入
		//根據類型注入
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

我們以根據名稱注入的舉例。
進入autowireByName方法。可以看到,其實很簡單。就是根據屬性的名稱去IOC容器中查找相對應的bean對象。
然後調用registerDependentBean(propertyName, beanName);方法註冊依賴的bean。

這個行代碼日後還要研究,這行代碼應該是解決Spring bean循環依賴問題的關鍵所在。
好了,我們繼續看依賴注入。得到依賴注入的對象後,下面就是將這些對象設置到待實例化的對象中。設置到對象中的方式也很簡單,就是調用屬性的set方法,用反射的方式注入進去。
接下來,我們看bean的生命週期方法。這個在面試中被問到的機率相當之高,大家一定要仔細看這一段。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
		//執行aware的方法
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
		//執行BeanPostProcessor的postProcessBeforeInitialization方法。我們熟知的ApplicationContextAware接口就是在這個步驟中,被IOC執行了setApplicationContext接口,將應用上下文進行注入
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
		//執行init-method方法。如果bean實現了InitializeBean接口的話,先執行該接口的afterPropertiesSet方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
		//執行BeanPostProcessor的postProcessAfterInitialization方法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

aware的方法,Spring一共執行了三個。

private void invokeAwareMethods(String beanName, Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

invokeInitMethods的方法實現爲:

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
			//執行afterPropertiesSet方法
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
					//利用反射執行自定義的初始化方法
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

說完了bean創建的生命週期,下面我們看一下bean銷燬的生命週期,bean銷燬的生命週期入口是doClose方法

protected void doClose() {
		// Check whether an actual close attempt is necessary...
		if (this.active.get() && this.closed.compareAndSet(false, true)) {
			if (logger.isInfoEnabled()) {
				logger.info("Closing " + this);
			}

			LiveBeansView.unregisterApplicationContext(this);

			try {
				// Publish shutdown event.
				publishEvent(new ContextClosedEvent(this));
			}
			catch (Throwable ex) {
				logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
			}

			// Stop all Lifecycle beans, to avoid delays during individual destruction.
			if (this.lifecycleProcessor != null) {
				try {
					this.lifecycleProcessor.onClose();
				}
				catch (Throwable ex) {
					logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
				}
			}

			// Destroy all cached singletons in the context's BeanFactory.
			destroyBeans();

			// Close the state of this context itself.
			closeBeanFactory();

			// Let subclasses do some final clean-up if they wish...
			onClose();

			// Reset local application listeners to pre-refresh state.
			if (this.earlyApplicationListeners != null) {
				this.applicationListeners.clear();
				this.applicationListeners.addAll(this.earlyApplicationListeners);
			}

			// Switch to inactive.
			this.active.set(false);
		}
	}

最終的銷燬方法實現在DisposableBeanAdapter類的destroy方法中

@Override
	public void destroy() {
		if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
			for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
				processor.postProcessBeforeDestruction(this.bean, this.beanName);
			}
		}
		//執行實現了DisposableBean接口的destroy方法
		if (this.invokeDisposableBean) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
			}
			try {
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((DisposableBean) this.bean).destroy();
						return null;
					}, this.acc);
				}
				else {
					((DisposableBean) this.bean).destroy();
				}
			}
			catch (Throwable ex) {
				String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
				if (logger.isDebugEnabled()) {
					logger.warn(msg, ex);
				}
				else {
					logger.warn(msg + ": " + ex);
				}
			}
		}

		if (this.destroyMethod != null) {
		//執行自定義的銷燬方法,destroy-method方法
			invokeCustomDestroyMethod(this.destroyMethod);
		}
		else if (this.destroyMethodName != null) {
			Method methodToCall = determineDestroyMethod(this.destroyMethodName);
			if (methodToCall != null) {
				invokeCustomDestroyMethod(methodToCall);
			}
		}
	}

好了,以上就是對IOC的解析。Spring小白,如果有錯誤,歡迎大家指正。

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