天天看點

Spring源碼(IOC子產品)分析

    前言:Spring是我們日常Java web開發必不可少的架構,有着很優秀的設計思想,在熟練使用它的基礎上,很有必要了解其源碼,Spring主要有七大子產品,分别為(IOC容器)Spring Core,應用上下文(Spring Context),Spring面向切面程式設計(Spring AOP),JDBC和DAO子產品(Spring DAO),對象實體映射(Spring ORM),Web子產品(Spring Web),MVC子產品(Spring Web MVC)。最核心的子產品為IOC和AOP,也是面試常問及的内容,本文将從比較宏觀的角度分析IOC的源碼,不會太深入一些細節,希望能起到抛磚引玉的作用。

    IOC也就是控制反轉,讓對象的建立的權力反轉給IOC容器,具體通過Spring配置來建立對象,它的底層原理包括xml配置檔案,dom4j解析,工廠設計模式,反射等等。使用Spring IOC容器,可以通過使用注解或xml配置的方式,Spring容器在啟動的時候,先會從xml配置或者注解中儲存所有注冊進來的Bean定義資訊。很經典的xml方式配置bean方式如下

<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  <bean id="boss" class="com.baobaotao.simple.Boss"></bean> 
</beans>
           

  由于現在普遍是Spring Boot項目,xml配置的方式也漸漸被注解方式取代了,注解方式更加友善,避免了xml檔案一些繁瑣的配置,是以分析Spring源碼過程中也是以注解方式,但是原理是一樣的,配置xml隻是多了一層解析xml檔案罷了。

  OK,我們先看看bean的建立。這是主類

public class SpringIOCTest {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(IOCConfig.class);
		// 擷取Person bean
		Person bean = applicationContext.getBean(Person.class);
		System.out.println(bean.toString());
	}
}
           

配置類:

@Configuration  //告訴Spring這是一個配置類
public class IOCConfig {
	//給容器中注冊一個Bean
	@Bean
	public Person person(){
		return new Person("lisi", 20);
	}
}
           

這就配置好一個Person類 bean,是不是很簡單!AnnotationConfigApplicationContext是注解的Spring上下文類,加載配置類,就可以完成配置,運作主類,就可以從Spring上下文中擷取到Person類對象!下面就分析下整個過程的原理。

進入AnnotationConfigApplicationContext構造方法,可以看到有三個方法,this()是調用無參構造函數,初始化讀取Bean定義資訊的reader以及Bean定義包掃描對象,可以掃描一些@Componet,@Service等注解标注的元件,register方法就是注冊配置類,關鍵是下面refresh()函數

Spring源碼(IOC子產品)分析
@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();
			}

			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.
				destroyBeans();

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

				// Propagate exception to caller.
				throw ex;
			}

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

下面逐一分析下refresh函數裡面做的事情

  1. prepareRefresh()函數是重新整理前的預處理,裡面做的工作主要是初始化一些屬性設定;子類自定義個性化的屬性設定方法;檢驗屬性的合法等等
  2. obtainFreshBeanFactory()的作用是擷取BeanFactory,Spring建立Bean可以直接Bean相關參數建立,也可以通過BeanFactory的getObject方法擷取Bean
  3. prepareBeanFactory(beanFactory);這裡是BeanFactory的預準備工作(BeanFactory進行一些設定),如:

1)設定BeanFactory的類加載器、支援表達式解析器

2)添加部分BeanPostProcessor。BeanPostProcessor的作用是很強大的,它能在Bean初始化的前後做一些工作,例如ApplicationContextAwareProcessor的工作就是在其方法postProcessBeforeInitialization()中對繼承自ApplicationContextAware的bean進行處理,調用其setApplicationContext将Spring上下文注入。是以我們可以簡單寫一個實作ApplicationContextAware接口類重寫setApplicationContext方法就可以得到Spring上下文來做一些事情了。

4.     postProcessBeanFactory(beanFactory)是BeanFactory準備工作完成之後進行的設定處理工作。子類可以通過重寫這個方法來在BeanFactory建立并預準備完成以後做進一步的設定操作,如下面的MyBeanFactoryPostProcessor
           
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory...");
		int count = beanFactory.getBeanDefinitionCount();
		String[] names = beanFactory.getBeanDefinitionNames();
		System.out.println("目前BeanFactory中有"+count+" 個Bean");
		System.out.println(Arrays.asList(names));
	}

}
           
5.     invokeBeanFactoryPostProcessor(beanFactory)為執行BeanFactoryPostProcessor的方法。BeanFactoryPostProcessor為BeanFactory的後置處理器,是在BeanFactory初始化之後執行的。
 6.     registerBeanPostProcessors(beanFactory)這步是注冊BeanPostProcessor,注意這是Bean的後置處理器,差別是BeanFactoryPostProcessor。兩者針對的對象不一樣,前者是針對Bean建立對象的方式,後者則是BeanFactory建立Bean的方式(Spring通過BeanFactory的getObject的方法擷取Bean對象).不同接口類型的BeanPostProcessor;在Bean建立前後的執行時機是不一樣的

        1)、擷取所有的 BeanPostProcessor;後置處理器都預設可以通過PriorityOrdered、Ordered接口來執行優先級
        2)、先注冊PriorityOrdered優先級接口的BeanPostProcessor;把每一個BeanPostProcessor;添加到BeanFactory中
   beanFactory.addBeanPostProcessor(postProcessor);
        3)、再注冊Ordered接口的
        4)、最後注冊沒有實作任何優先級接口的
        5)、最終注冊MergedBeanDefinitionPostProcessor;
        6)、注冊一個ApplicationListenerDetector;來在Bean建立完成後檢查是否是ApplicationListener,如果是
   applicationContext.addApplicationListener((ApplicationListener<?>) bean)
           

 7.          initMessageSource()主要是初始化MessageSource元件,來做國際化功能、消息綁定、消息解析

 8.          onRefresh()方法是空的,設計的目的主要是給子類重寫,在容器重新整理的時候可以自定義邏輯

 9.          finishBeanFactoryInitialization(beanFactory):初始化所有剩下的單執行個體Bean 。

具體過程如下:
 1、beanFactory.preInstantiateSingletons();初始化後剩下的單執行個體bean
   1)、擷取容器中的所有Bean,依次進行初始化和建立對象
   2)、擷取Bean的定義資訊;RootBeanDefinition
   3)、Bean不是抽象的,是單執行個體的,是懶加載;
      1)、判斷是否是FactoryBean;是否是實作FactoryBean接口的Bean;
      2)、不是工廠Bean。利用getBean(beanName);建立對象
         0、getBean(beanName); ioc.getBean();
         1、doGetBean(name, null, null, false);
         2、先擷取緩存中儲存的單執行個體Bean。如果能擷取到說明這個Bean之前被建立過(所有建立過的單執行個體Bean都會被緩存起來)
            從private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);擷取的
         3、緩存中擷取不到,開始Bean的建立對象流程;
         4、标記目前bean已經被建立
         5、擷取Bean的定義資訊;
         6、【擷取目前Bean依賴的其他Bean;如果有按照getBean()把依賴的Bean先建立出來;】
         7、啟動單執行個體Bean的建立流程;
            1)、createBean(beanName, mbd, args);
            2)、Object bean = resolveBeforeInstantiation(beanName, mbdToUse);讓BeanPostProcessor先攔截傳回代理對象;
               【InstantiationAwareBeanPostProcessor】:提前執行;
               先觸發:postProcessBeforeInstantiation();
               如果有傳回值:觸發postProcessAfterInitialization();
            3)、如果前面的InstantiationAwareBeanPostProcessor沒有傳回代理對象;調用4)
            4)、Object beanInstance = doCreateBean(beanName, mbdToUse, args);建立Bean
                1)、【建立Bean執行個體】;createBeanInstance(beanName, mbd, args);
                  利用工廠方法或者對象的構造器建立出Bean執行個體;
                2)、applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                  調用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType, beanName);
                3)、【Bean屬性指派】populateBean(beanName, mbd, instanceWrapper);
                  指派之前:
                  1)、拿到InstantiationAwareBeanPostProcessor後置處理器;
                     postProcessAfterInstantiation();
                  2)、拿到InstantiationAwareBeanPostProcessor後置處理器;
                     postProcessPropertyValues();
                  =====指派之前:===
                  3)、應用Bean屬性的值;為屬性利用setter方法等進行指派;
                     applyPropertyValues(beanName, mbd, bw, pvs);
                4)、【Bean初始化】initializeBean(beanName, exposedObject, mbd);
                  1)、【執行Aware接口方法】invokeAwareMethods(beanName, bean);執行xxxAware接口的方法
                     BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
                  2)、【執行後置處理器初始化之前】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
                     BeanPostProcessor.postProcessBeforeInitialization();
                  3)、【執行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
                     1)、是否是InitializingBean接口的實作;執行接口規定的初始化;
                     2)、是否自定義初始化方法;
                  4)、【執行後置處理器初始化之後】applyBeanPostProcessorsAfterInitialization
                     BeanPostProcessor.postProcessAfterInitialization();
                5)、注冊Bean的銷毀方法;
            5)、将建立的Bean添加到緩存中singletonObjects;
         ioc容器就是這些Map;很多的Map裡面儲存了單執行個體Bean,環境資訊。。。。;
   所有Bean都利用getBean建立完成以後;
      檢查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就執行afterSingletonsInstantiated();
           

10.        finishRefresh()這一步就是完成BeanFactory的初始化建立工作,IOC容器到這裡就建立完成了。

      總結:

            1. Spring容器在啟動的時候,先會儲存所有注冊進來的Bean定義資訊,Bean定義資訊有兩大來源,一個就是xml配置了,就是本文剛開始提到的Bean配置方式,另一種就是注解注冊Bean!就是平常我們開發常用到的@Service、@Componet、@Bean等等注解

            2.Spring容器會在合适的時機建立這些Bean,建立好就儲存在容器中,如果是單例的Bean會緩存下來。最後到了統一建立剩下的Bean的時候,就是前面所提到的方法:finishBeanFactoryInitialization()

           3.後置處理器:BeanProcessor非常重要和強大,每一個Bean建立完成,都會使用各種後置處理器進行處理,進而達到增強Bean的功能,Spring另一個強大的子產品AOP面向切面程式設計也依靠到了BeanProcessor來實作

           4.上面其實還有某一兩個函數沒有提到,比如initApplicationEventMulticaster(),這個函數涉及到Spring事件驅動模型,來進行事件的監聽,派發。