1. 程式人生 > 其它 >Spring原始碼分析之ApplicationContext

Spring原始碼分析之ApplicationContext

前言

通過前面的部落格我們已經對Spring的IOC容器有了一定的瞭解,它的底層實現為DefaultListableBeanFactory,這是一個BeanFactory,
ApplicationContext在BeanFactory容器的基礎上又增加了很多功能,如事件分發,國際化等。相關類圖如下

可以看到ApplicationContext是BeanFactory的子介面,但是它沒有自己實現這些方法,對於Bean的操作,都是委託給BeanFactory來處理的。
ApplicationContext有很多實現類,如支援XML配置的ClassPathXmlApplicationContext,支援註解配置的AnnotationConfigApplicationContext,
支援內嵌Tomcat和註解的AnnotationConfigServletWebServerApplicationContext(SpringBoot預設使用)。
這裡我們以ClassPathXmlApplicationContext為例來分析,AnnotationConfigApplicationContext對於註解的處理額外做了很多工作,不利於分析流程。

簡單使用

首先在classpath下建立一個XML配置檔案

<?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.xsd">

  <bean id="userService"
    class="com.imooc.sourcecode.java.spring.ioc.test4.TestXmlContext.UserService"/>
</beans>

使用ClassPathXmlApplicationContext來解析此配置檔案

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestXmlContext {

  public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
    context.setConfigLocation("spring.xml");
    context.refresh();
    UserService userService = (UserService) context.getBean("userService");
    userService.userList();//userList
  }

  public static class UserService {

    public void userList() {
      System.out.println("userList");
    }
  }
}

使用方式很簡單,但Spring內部幫我們做了很多工作。

原始碼分析

核心為refresh()方法,進入父類AbstractApplicationContext中

@Override
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//做一些準備工作,主要是environment物件的建立
			prepareRefresh();

			//建立DefaultListableBeanFactory容器,並從XML配置檔案中載入BeanDefinition,註冊到容器中
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//配置BeanFactory,設定ClassLoader,設定SpringEL表示式解析器,向BeanFactory中新增特定的BeanPostProcessor
			prepareBeanFactory(beanFactory);

			try {
				//對beanFactory做一些處理,留給子類擴充套件
				postProcessBeanFactory(beanFactory);

				//從容器中獲取所有型別為BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的Bean,並執行他們的擴充套件方法
                                //這兩個類是Spring提供的兩種擴充套件
                                //ConfigurationClassPostProcessor(用來解析@Configuration類,註解自動掃描等)就是在這一步執行的
				invokeBeanFactoryPostProcessors(beanFactory);

				//從容器中獲取所有型別為BeanPostProcessor的Bean,並設定到BeanFactory中(呼叫BeanFactory的addBeanPostProcessor()方法)
                                //最重要的AutowiredAnnotationBeanPostProcessor(處理@Autowired註解)就是在這新增的
				registerBeanPostProcessors(beanFactory);

				//初始化MessageSource,這是一個支援國際化的元件
				initMessageSource();

				//初始化事件分發器,預設型別為SimpleApplicationEventMulticaster,通過它我們可以實現事件的訂閱和釋出
				initApplicationEventMulticaster();

				//留給子類擴充套件,ServletWebServerApplicationContext(SpringBoot使用的是它的子類)在這一步建立了WebServer(預設是Tomcat),注意,還沒啟動
				onRefresh();

				//從容器中獲取所有型別為ApplicationListener的Bean,並註冊到事件分發器中
                                //SpringBoot會從spring.factories中獲取配置的ApplicationListener列表,
                                //如ConfigFileApplicationListener(用來解析application.yml檔案或application.properties檔案)
				registerListeners();

				//例項化所有非懶載入的單例Bean,其實就是依次呼叫getBean(name)方法,BeanFactory會在第一次呼叫時建立Bean
				finishBeanFactoryInitialization(beanFactory);

				//例項化DefaultLifecycleProcessor物件(這是一個生命週期處理器),
                                //釋出ContextRefreshedEvent事件(監聽此事件的ApplicationListener都會處理)
                                //ServletWebServerApplicationContext(SpringBoot使用的是它的子類)在這一步啟動WebServer(預設是Tomcat)
				finishRefresh();
			}

			catch (BeansException ex) {
				//如果丟擲異常,銷燬所有已經建立的Bean
				destroyBeans();

				//重置active標識
				cancelRefresh(ex);

				throw ex;
			}

			finally {
				//清空快取,如反射的快取
				resetCommonCaches();
			}
		}
	}

此方法執行完,我們的ApplicationContext物件可以算是初始化完成了,所有的非懶載入Bean也已經建立完成了。
上面的每一個方法實現都是比較複雜的,細節很多,這裡我們再簡單分析一下obtainFreshBeanFactory()方法。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
                //建立並初始化BeanFactory
		refreshBeanFactory();
		return getBeanFactory();
	}

繼續跟進去refreshBeanFactory()方法,AbstractRefreshableApplicationContext類重寫了此方法

@Override
protected final void refreshBeanFactory() throws BeansException {
                //如果已經建立過BeanFactory,銷燬所有Bean
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
                        //建立一個新的beanFactory,可以看到實現為DefaultListableBeanFactory型別
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
                        //從XML配置檔案中載入BeanDefinition
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
	}

分析總結

從上面的類圖可以看到,ApplicationContext介面繼承ApplicationEventPublisher(事件分發器),MessageSource(國際化相關),BeanFactory(IOC容器),
但ApplicationContext的實現類本身沒有實現這些介面的方法,而是委託給對應的實現類,如事件分發委託給SimpleApplicationEventMulticaster,
國際化委託給ResourceBundleMessageSource(我們需要自己定義此Bean),IOC容器委託給DefaultListableBeanFactory。

@Override
public Object getBean(String name) throws BeansException {
		return getBeanFactory().getBean(name);
	}

以getBean()方法為例,可以看到確實是交給內部的BeanFactory來處理的。

關於國際化,可以檢視Spring對國際化的支援

參考

Spring IOC 容器原始碼分析