在前面咱們簡單的分析了BeanFactory的結構,ListableBeanFactory,HierarchicalBeanFactory,AutowireCapableBeanFactory。主要核心類DefaultListableBeanFactory,經過編程啓動IOC容器 將BeanFactory的功能逐漸的剝離開來,方便咱們理解整個架構。java
ClassPathResource resource = new ClassPathResource("spring.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(resource); MyBean bean = factory.getBean(MyBean.class); System.out.println(bean.toString());
DefaultListableBeanFactory 實現了 BeanDefinitionRegistry
接口,具備了註冊bean的功能,node
讀取資源則經過單獨的模塊來實現,這裏委託爲XmlBeanDefinitionReader
來讀取xml配置文件spring
在前面咱們能夠很方便的經過編程的方式來手工控制這些配置的容器的創建過程了,可是,在Spring 中,系統以及爲咱們提供了許多已經定義好的容器的實現,若是說BeanFactory是Spring的"心臟",那麼ApplicationContext就是完整的"身軀"了。ApplicationContext由BeanFactory派生而來,提供了更多面向實際應用的功能,因此說,ApplicationContext是一個高級形態意義的IOC容器,下面咱們就來慢慢的認識一下它。編程
再來看一段代碼:數據結構
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); MyBean bean = context.getBean(MyBean.class); System.out.println(bean.toString());
這個看起來是否是比 DefaultListableBeanFactory 簡單多了,其實所謂的高級容器也就是把最基礎的容器進行了封裝,所以在最開始時咱們才使用最基礎的BeanFactory來展現,這樣更容易理解。架構
看到如上的繼承體系,應該就更能明白ApplicationContext 是Spring BeanFactory的高級形態的容器了。app
@Nullable String getId(); /** * Return a name for the deployed application that this context belongs to. * @return a name for the deployed application, or the empty String by default */ String getApplicationName(); /** * Return a friendly name for this context. * @return a display name for this context (never {@code null}) */ String getDisplayName(); /** * Return the timestamp when this context was first loaded. * @return the timestamp (ms) when this context was first loaded */ long getStartupDate(); /** * Return the parent context, or {@code null} if there is no parent * and this is the root of the context hierarchy. * @return the parent context, or {@code null} if there is no parent */ @Nullable ApplicationContext getParent(); /** * Expose AutowireCapableBeanFactory functionality for this context. * <p>This is not typically used by application code, except for the purpose of * initializing bean instances that live outside of the application context, * applying the Spring bean lifecycle (fully or partly) to them. * <p>Alternatively, the internal BeanFactory exposed by the * {@link ConfigurableApplicationContext} interface offers access to the * {@link AutowireCapableBeanFactory} interface too. The present method mainly * serves as a convenient, specific facility on the ApplicationContext interface. * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException * after the application context has been closed.</b> In current Spring Framework * versions, only refreshable application contexts behave that way; as of 4.2, * all application context implementations will be required to comply. */ AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
在ApplicationContext容器中,咱們以經常使用的ClassPathXmlApplicationContext
的實現來分析ApplicationContext容器的設計原理框架
在ClassPathXmlApplicationContext 的設計中,其主要的功能在基類AbstractXmlApplicationContext
中已經實現了。ide
public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
ClassPathXmlApplicationContext 看起來仍是很簡單的,主要的實現都在基類裏面實現了,這是隻是負責調用。源碼分析
父容器的設置在 AbstractApplicationContext
中
/** Parent context */ @Nullable private ApplicationContext parent; @Override public void setParent(@Nullable ApplicationContext parent) { this.parent = parent; if (parent != null) { Environment parentEnvironment = parent.getEnvironment(); if (parentEnvironment instanceof ConfigurableEnvironment) { getEnvironment().merge((ConfigurableEnvironment) parentEnvironment); } } }
若是存在父容器,那麼會合並二者的環境配置Environment。這裏Environment並非咱們的重點。
這個 refresh()過程會牽涉IOC容器啓動的一系列複雜操做,同時,對於不一樣容器的實現,這些操做都是相似的,所以在基類中將它們封裝好。因此,咱們在ClassPathXmlApplicationContext 的設計中看到的只是一個簡單的調用。關於這個refresh()在IOC容器啓動時的具體實現,這個在後面再來分析,這裏就不展開了。
簡單來講,IOC容器的初始化由前面介紹的refresh()方法來啓動的,這個方法標誌着IOC容器的正式啓動。具體來講,這個啓動包括BeanDefinition的Resouce定位、載入和註冊三個基本過程。若是咱們瞭解如何編程式地使用IOC容器,就能夠清楚得看到Resource定位和載入過程的接口調用。在下面的內容裏,咱們將會詳細分析這三個過程的實現。
Spring把這三個過程分開,並使用不一樣的模塊來完成,如使用響應的ResourceLoader、BeanDefinitionReader等模塊,經過這樣的設計方式,可讓用戶更加靈活地對這三個過程進行剪裁或擴展,定義出最適合本身的IOC容器的初始化過程。
Resource 定位
第一個過程就是Resource 定位過程。這個Resource 定位指的是BeanDefinition的資源定位,它由ResourceLoader經過統一的Resource接口來完成,這個Resource對各類形式的BeanDefinition的使用都提供了統一接口。
BeanDefinition的載入
第二個過程是BeanDefinition的載入。這個載入過程是把用戶定義好的Bean表示成IOC容器內部的數據結構,而這個容器內部的數據結構就是BeanDefinition,具體來講,這個BeanDefinition實際上就是POJO對象在IOC容器中的抽象,經過這個BeanDefinition定義的數據結構,使IOC容器可以方便地對POJO對象也就是Bean進行管理。
註冊BeanDefinition
第三個過程是向IOC容器註冊這些BeanDefinition的過程,這個過程是經過調用BeanDefinitionRegistry接口的實現來完成的。這個註冊過程把載入過程當中解析獲得的BeanDefinition向IOC容器進行註冊,經過分析,咱們能夠看到,在IOC容器內部將BeanDefinition注入到一個ConcurrentHashMap中去,IOC容器就是經過這個HashMap來持有這些BeanDefinition數據的。
以編程的方式使用DefaultListableBeanFactory時,首先定義一個Resource來定位容器使用的BeanDefinition。這時使用的是ClassPathResource,這意味着Spring會在類路徑中去尋找以文件形式存在的BeanDefinition信息。
ClassPathResource resource = new ClassPathResource("spring.xml");
這裏定義的Resource 並不能由DefaultListableBeanFactory直接使用,Spring經過BeanDefinitionReader來對這些信息進行處理。在這裏,咱們也能夠看到使用ApplicationContext相對於直接使用DefaultListableBeanFactory的好處。由於ApplicationContext 中,Spring已經爲咱們提供了一系列加載不一樣Resource的讀取器的實現,而DefaultListableBeanFactory只是一個純粹的IOC容器,須要爲它配置特定的讀取器才能完成這些功能。固然,有利就有弊,使用DefaultListableBeanFactory 這種更底層的容器,能提升定製IOC容器的靈活性。
下面以ClassPathXmlApplicationContext 爲例,經過分析這個ApplicationContext的實現來看看它是怎樣完成這個Resource定位過程的。
這個ClassPathXmlApplicationContext 已經經過繼承AbstractApplicationContext具有了ResourceLoader讀入Resource定義的BeanDefinition的能力,由於AbstractApplicationContext的基類是DefaultResourceLOader。
在前面ClassPathXmlApplicationContext 的源碼中,咱們知道核心代碼在refresh方法裏面。下面是refresh的代碼,可是並不會詳細分析,咱們的工做主要仍是拆,目前只分析咱們須要的。
AbstractApplicationContext -> refresh():
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 裏面就是Spring的啓動流程,在 refresh -> obtainFreshBeanFactory 會建立一個BeanFactory,而obtainFreshBeanFactory在子類AbstractRefreshableApplicationContext中實現。
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //載入BeanDefinition,其他的方法暫時不分析 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
在這個方法中,經過 createBeanFactory 構建一個IOC容器供ApplicationContext 使用。這個IOC容器就是咱們前面提到過的DefaultListableBeanFactory,同時,它啓動了loadBeanDefinitions 來載入BeanDefinition,這個過程和前面用編程式的方法來使用IOC(XmlBeanFactory)的過程很是相似。
AbstractXmlApplicationContext ->loadBeanDefinitions:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
設置BeanDefinitionReader,由於AbstractApplicationContext 繼承DefaultResourceLoader ,所以ResourceLoader 能夠設置成this,繼續跟蹤代碼,在 AbstractBeanDefinitionReader -> loadBeanDefinitions中:
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //在載入和解析中分析 int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } return loadCount; } catch (IOException ex) { //...省略 } } else { //...省略代碼 } }
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
經過 resourceLoader來獲取配置資源,而這個resourceLoader 就是 ClassPathXmlApplicationContext,這個getResources 方法在父類AbstractApplicationContext中實現
AbstractApplicationContext -> getResources:
public Resource[] getResources(String locationPattern) throws IOException { return this.resourcePatternResolver.getResources(locationPattern); }
resourcePatternResolver 在初始化的時候,被設置成了 PathMatchingResourcePatternResolver
public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver(); } protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); }
這樣就能夠經過PathMatchingResourcePatternResolver 來獲取資源了。
在完成BeanDefinition的Resource定位的分析後,下面來了解整個BeanDefinition信息的載入過程。對IOC容器來講,這個載入過程,至關於把定義的BeanDefinition在IOC容器中轉化成一個Spring內部表示的數據結構的過程。IOC容器對Bean的管理和依賴注入功能的實現,是經過對其持有的BeanDefinition進行各類相關操做來完成的。這些BeanDefinition數據在IOC容器中經過一個HashMap來保持和維護。
在前面,咱們定位資源的時候,展現了AbstractBeanDefinitionReader 中的loadBeanDefinitions 方法,在裏面會調用下面的代碼:
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
但這個方法在AbstractBeanDefinitionReader 類裏是沒有實現的,它是一個接口方法,具體的實如今 XmlBeanDefinitionReader中,在讀取器中,須要獲得表明XML文件的Resource,由於這個Resource對象封裝了對XML文件的I/O操做,因此讀取器能夠在打開I/O流後獲得XML的文件對象,有了這個對象文件之後,就能夠按照Spring的Bean定義規則來對這個XML的文檔樹進行解析了,這個解析是交給BeanDefinitionParserDelegate來完成的。
XmlBeanDefinitionReader -> loadBeanDefinitions:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { //省略代碼 } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { //省略代碼 } finally { //省略代碼 } }
接着看doLoadBeanDefinitions 方法:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //獲取XML文件的Document對象 Document doc = doLoadDocument(inputSource, resource); //對BeanDefinition解析的過程 return registerBeanDefinitions(doc, resource); } //省略部分代碼 }
這裏咱們就不去分析如何獲得Document對象的了,咱們關心的是Spring的BeanDefinion是怎麼按照Spring的Bean語義要求進行解析並轉化爲容器內部數據機構的,這個過程是在registerBeanDefinitions 中完成的。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //經過 BeanDefinitionDocumentReader 對XML的BeanDefinition 進行解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //具體解析過程 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
BeanDefinition的載入分紅兩部分,首先經過調用XML的解析器獲得document對象,但這些document對象並無按照Spring的Bean規則進行解析。在完成通用的XML解析事後,纔是按照Spring的Bean規則進行解析的地方,這個按照Spring的Bean規則進行解析的過程是在documentReader中實現的,這裏使用的documentReader是默認配置好的DefaultBeanDefinitionDocumentReader。
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanUtils.instantiateClass(this.documentReaderClass); }
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
在 DefaultBeanDefinitionDocumentReader-> parseDefaultElement解析了配置
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
對bean的配置的解析處理是經過processBeanDefinition 方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 將 <bean /> 節點中的信息提取出來,而後封裝到一個 BeanDefinitionHolder 中 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 若是有自定義屬性的話,進行相應的解析,先忽略 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //這裏是向IOC容器註冊BeanDefinition BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { //省略代碼 } //在BeanDefinition向IOC容器註冊完後,發送消息 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
提取出來的信息結果由BeanDefinitionHolder對象來持有。這個BeanDefinitionHolder除了持有BeanDefinition對象外,還持有其餘與BeanDefinition的使用相關的信息,好比Bean的名字,別名集合等。
public class BeanDefinitionHolder implements BeanMetadataElement { private final BeanDefinition beanDefinition; private final String beanName; private final String[] aliases; ...
具體的Spring BeanDefinition的解析是在BeanDefinitionParserDelegate 中完成的,這個類包含了對各類Spring Bean 定義的規則的處理,這裏咱們暫且就不深刻了。
前面已經分析過了BeanDefinition在IOC容器中載入和解析的過程。在這些動做完成之後,用戶定義的BeanDefinition信息已經在IOC容器內創建起了本身的數據結構以及相應的數據表示,但此時這些數據還不能供IOC容器直接使用,須要在IOC容器中對這些BeanDefinition數據進行註冊,這個註冊爲IOC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是經過一個ConcurrentHashMap來持有載入的BeanDefinition的。
在DefaultBeanDefinitionDocumentReader ->processBeanDefinition 中經過以下代碼註冊:
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // registry 就是 DefaultListableBeanFactory registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 若是還有別名的話,也要根據別名所有註冊一遍,否則根據別名就會找不到 Bean 了 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { // alias -> beanName 保存它們的別名信息,這個很簡單,用一個 map 保存一下就能夠了, // 獲取的時候,會先將 alias 轉換爲 beanName,而後再查找 registry.registerAlias(beanName, alias); } } }
查看 DefaultListableBeanFactory 中的 registerBeanDefinition 方法:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //省略了部分代碼 // 檢查BeanDefinition 是否已經存在 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { //若是不容許覆蓋 if (!isAllowBeanDefinitionOverriding()) { //拋異常 } else if (existingDefinition.getRole() < beanDefinition.getRole()) { //可參考 BeanFactory 源碼分析(1)中的BeanDefinition分析 //用框架定義的 Bean 覆蓋用戶自定義的 Bean } else if (!beanDefinition.equals(existingDefinition)) { //用新的 Bean 覆蓋舊的 Bean } else { //用同等的 Bean 覆蓋舊的 Bean,這裏指的是 equals 方法返回 true 的 Bean } // 覆蓋 this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 判斷是否已經有其餘的 Bean 開始初始化了. // 注意,"註冊Bean" 這個動做結束,Bean 依然尚未初始化 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // 通常會進到這個分支。 // 將 BeanDefinition 放到這個 map 中,這個 map 保存了全部的 BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); // 這是個 ArrayList,因此會按照 bean 配置的順序保存每個註冊的 Bean 的名字 this.beanDefinitionNames.add(beanName); // 這是個 LinkedHashSet,表明的是手動註冊的 singleton bean, // 注意這裏是 remove 方法,到這裏的 Bean 固然不是手動註冊的 // 手動指的是經過調用如下方法註冊的 bean : // registerSingleton(String beanName, Object singletonObject) // 這不是重點,Spring 會在後面"手動"註冊一些 Bean, // 如 "environment"、"systemProperties" 等 bean,咱們本身也能夠在運行時註冊 Bean 到容器中的 this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } }
省略了部分代碼,這裏主要展現的大體流程,對於具體的細節這裏並無涉及。如今咱們已經分析完BeanDefinition的載入和註冊了,此時依賴注入並無發生,依賴注入發生在應用第一次向容器索要bean時,固然能夠設置Bean的lazy-init屬性來控制預實例化的過程,這裏咱們並不會分析依賴注入過程,只是大體梳理了IOC容器的初始化過程,後面會再次深刻這部分,一點一點的解剖。
在分析了底層的BeanFactory後,咱們分析了高級形態的BeanFactory-ApplicationContext,ApplicationContext其實就是將其餘功能集成了起來,使得BeanFactory不只僅是一個容器了,ApplicationContext具備了下面的特性:
隨後咱們以ClassPathXmlApplicationContext爲例簡單的分析了BeanDefinition的資源定位,載入和解析以及註冊過程。