一、什么是IOC
引用 Spring 官方原文:This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) [1] principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.
“控制反轉(IoC)”也稱為“依賴注入(DI)”,是一個定義對象依賴的過程,對象只和 構造參數,工廠方法參數,對象實例屬性或工廠方法返回相關。容器在創建這些 bean 的時 候注入這些依賴。這個過程是一個反向的過程,所以命名為依賴反轉,對象實例的創建由其 提供的構造方法或服務定位機制來實現。
IOC 最大的好處就是“解耦”。
二、IOC底層原理使用技術
1、xml配置文件
2、dom4j解析配置的xml文件
3、工廠、策略、委托等設計模式
4、反射
5、動態代理(JDK自帶動態代理(實現了接口的類)、CGLib(未實現接口的類))
三、Spring容器基本概念
BeanFactory 是 Spring 框架的基礎設施,面向 Spring 本身;
ApplicationContext 面向使用 Spring 框架的開發者,幾乎所有的應用場合我們都直接使用 ApplicationContext 而非底層的 BeanFactory。
1、BeanFactory
Spring Bean的創建是典型的工廠模式,這一系列的Bean工廠,也即IOC容器為開發者管理對象間的依賴關系提供了很多便利和基礎服務,在Spring中有許多的IOC容器的實現供用戶選擇和使用。如圖:
其中BeanFactory作為最頂層的一個接口類,它定義了IOC容器的基本功能規范,BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是最終的默認實現類是 DefaultListableBeanFactory,他實現了所有的接口。那為何要定義這么多層次的接口呢?其實每個接口都有他使用的場合,它主要是為了區分在 Spring 內部在操作過程中對象的傳遞和轉化過程中,對對象的數據訪問所做的限制。例如:
- ListableBeanFactory 接口:表示這些 Bean 是可列表的。
- HierarchicalBeanFactory 接口:表示的是這些 Bean 是有繼承關系的,也就是每個Bean 有可能有父 Bean。
- AutowireCapableBeanFactory 接口:定義 Bean 的自動裝配規則。
這四個接口共同定義了 Bean 的集合、Bean 之間的關系、以及 Bean 行為。BeanFactory接口定義如下:
public interface BeanFactory { //對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象, //如果需要得到工廠本身,需要轉義 String FACTORY_BEAN_PREFIX = "&"; //根據bean的名字,獲取在IOC容器中得到bean實例 Object getBean(String name) throws BeansException; //根據bean的名字和Class類型來得到bean實例,增加了類型安全驗證機制。 Object getBean(String name, Class requiredType) throws BeansException; //提供對bean的檢索,看看是否在IOC容器有這個名字的bean boolean containsBean(String name); //根據bean名字得到bean實例,並同時判斷這個bean是不是單例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; //得到bean實例的Class類型 Class getType(String name) throws NoSuchBeanDefinitionException; //得到bean的別名,如果根據別名檢索,那么其原名也會被檢索出來 String[] getAliases(String name); }
2、BeanDefinition
SpringIOC容器管理了我們定義的各種Bean對象及其相互的關系。在BeanFactory
容器中,每一個注入對象都對應一個BeanDefinition
實例對象,該實例對象負責保存注入對象的所有必要信息,包括其對應的對象的class類型、是否是抽象類、構造方法參數以及其他屬性等。當客戶端向BeanFactory
請求相應對象的時候,BeanFactory
會通過這些信息為客戶端返回一個完備可用的對象實例。其繼承體系如下:
那么BeanDefinition
實例對象的信息是從哪而來呢?這里就要引出一個專門加載解析配置文件的類了,他就是BeanDefinitionReader
,對應到xml
配置文件,就是他的子類XmlBeanDefinitionReader
,XmlBeanDefinitionReader
負責讀取Spring
指定格式的XML
配置文件並解析,之后將解析后的文件內容映射到相應的BeanDefinition
。這個解析過程主要通過下圖中的類完成:
3、XmlBeanFactory
在BeanFactory里只對IOC容器的基本行為作了定義,根本不關心bean是如何定義、加載、生產的。工廠是如何產生對象的,我們需要看具體的IOC容器實現,spring提供了許多IOC容器的實現。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是針對最基本的ioc容器的實現,這個IOC容器可以讀取XML文件定義的BeanDefinition(XML文件中對bean的描述),因此XmlBeanFactory是一個低級容器。
XmlBeanFactory 初級容器是通過 Resource 裝載 Spring 配置信息並啟動 IOC 容器,然后就可以通過 BeanFactory#getBean(beanName)方法從 IOC 容器中獲取 Bean 了。通過 BeanFactory 啟動IOC 容器時,並不會初始化配置文件中定義的 Bean,初始化動作發生在第一次調用時。
對於單實例 (singleton) 的 Bean 來說,BeanFactory 會緩存 Bean 實例,所以第二次使用 getBean() 獲取 Bean 時將直接從 IOC 容器的緩存中獲取 Bean 實例。Spring 在 DefaultSingletonBeanRegistry 類中提供了一個用於緩存單實例 Bean 的緩存器,它是一個用ConcurrentHashMap 實現的緩存器,單實例的 Bean 以 beanName 為鍵保存在這個ConcurrentHashMap 中。
注意:在初始化 BeanFactory 時,必須為其提供一種日志框架,比如使用Log4J, 即在類路徑下提供 Log4J 配置文件,這樣啟動 Spring 容器才不會報錯。
public class XmlBeanFactory extends DefaultListableBeanFactory{ private final XmlBeanDefinitionReader reader; public XmlBeanFactory(Resource resource)throws BeansException{ this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException{ super(parentBeanFactory); this.reader = new XmlBeanDefinitionReader(this); this.reader.loadBeanDefinitions(resource); } }
XMLBeanFactory可以如下使用,實際上初級容器大致使用就是如下方法。
//根據Xml配置文件創建Resource資源對象,該對象中包含了BeanDefinition的信息 ClassPathResource resource =new ClassPathResource("application-context.xml"); //創建DefaultListableBeanFactory DefaultListableBeanFactory factory =new DefaultListableBeanFactory(); //創建XmlBeanDefinitionReader讀取器,用於載入BeanDefinition。之所以需要BeanFactory作為參數,是因為會將讀取的信息回調配置給factory XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory); //XmlBeanDefinitionReader執行載入BeanDefinition的方法,最后會完成Bean的載入和注冊。完成后Bean就成功的放置到IOC容器當中,以后我們就可以從中取得Bean來使用 reader.loadBeanDefinitions(resource);
4、ApplicationContext
ApplicationContext是Spring提供的一個高級IOC容器,它除了能夠提供IOC容器的基本功能外,還為用戶提供了以下的附加服務。從ApplicationContext接口的實現,我們看出其特點:
- 支持信息源,可以實現國際化。(實現MessageSource接口)
- 訪問資源。(實現ResourcePatternResolver接口,這個后面要講)
- 支持應用事件。(實現ApplicationEventPublisher接口)
其與BeanFactory關系如下:
Spring為ApplicationContext提供了3種實現,分別是ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext,其中XmlWebApplicationContext是專為Web工程定制的。如下圖:
ApplicationContext允許上下文嵌套,通過保持父上下文可以維持一個上下文體系。對於bean的查找可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上下文,逐級向上,這樣為不同的Spring應用提供了一個共享的bean定義環境。
5、ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
FileSystemXmlApplicationContext和ClassPathXmlApplicationContext是Spring定義的一種高級容器,不僅僅是IOC。它支持不同信息源頭,支持 BeanFactory 工具類,支持層級容器,支持訪問文件資源,支持事件發布通知,支持接口回調等。
ClassPathXmlApplicationContext的類外部結構關系為:
FileSystemXmlApplicationContext的類外部結構關系為(與ClassPathXmlApplicationContext一致):
與BeanFactory的xml文件定位方式一樣是基於路徑的。如:
AplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml"); //加載單個配置文件 String[] locations = {"bean1.xml", "bean2.xml", "bean3.xml"}; ApplicationContext ctx = new FileSystemXmlApplicationContext(locations ); //加載多個配置文件 ApplicationContext ctx =new FileSystemXmlApplicationContext("D:roject/bean.xml");//根據具體路徑加載文件 // ClassPathXmlApplicationContext同FileSystemXmlApplicationContext一樣的初始化方式 // FileSystemXmlApplicationContext這個方法是從文件絕對路徑加載配置文件,如果在參數中寫的不是絕對路徑,那么方法調用的時候也會默認用絕對路徑來找, // 默認文件路徑是項目名下一級,與src同級。如果前邊加了file:則說明后邊的路徑就要寫全路徑了,如:file:D:/workspace/applicationContext.xml。 // ClassPathXmlApplicationContext這個方法是從classpath下加載配置文件(適合於相對路徑方式加載),默認文件路徑是src下面那一級。 // 該方法參數中classpath: 前綴是不需要的,默認就是指項目的classpath路徑下面;如果要寫classpath,請注意classpath:和classpath*:的區別: // classpath: 只能加載一個配置文件,如果配置了多個,則只加載第一個 // classpath*: 可以加載多個配置文件,如果有多個配置文件,就用這個
6、XMLWebApplicationContext
XMLWebApplicationContext是專門為web應用准備的,他允許從相對於web根目錄的路勁中裝載配置文件完成初始化工作,從XMLWebApplicationContext中可以獲得ServletContext的引用,整個Web應用上下文對象將作為屬性放置在ServletContext中,以便web應用可以訪問spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext serviceContext)方法來獲得XMLWebApplicationContext對象。
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
ContextLoaderListener和DispatcherServlet 會創建XMLWebApplicationContext容器,但是ContextLoaderListener監聽器初始化完畢后,才會初始化Servlet,:
XMLWebApplicationContext的類外部結構關系為:
四、Spring IOC容器初始化
ApplicationContext 容器的初始化流程主要由 AbstractApplicationContext 類中的 refresh 方法實現。大致過程為:為 BeanFactory 對象執行后續處理(如:context:propertyPlaceholder等)->在上下文(Context)中注冊 bean->為 bean 注冊攔截處理器(AOP 相關)->初始化上 下文消息(初始化 id 為 messgeSource 的國際化 bean 對象)->初始化事件多播(處理事件監 聽,如ApplicationEvent等)->初始化主題資源(SpringUI 主題 ThemeSource)->注冊自定義 監聽器->實例化所有非 lazy-init 的 singleton 實例->發布相應事件(Lifecycle 接口相關實現類的生命周期事件發布)。
在 spring 中,構建容器的過程都是同步的。同步操作是為了保證容器構建的過程中,不出現多線程資源沖突問題(因為對象的構建、資源的掃描、文件的掃描如果存在多線程對文件的掃描問題,會出現鎖的問題)。
1、構造函數觸發IOC初始化
從 FileSystemXmlApplicationContext 開始剖析,創建 FileSystemXmlApplicationContext 的構造方法:
public class Test { public static void main(String[] args) throws ClassNotFoundException { ApplicationContext ctx = new FileSystemXmlApplicationContext ("spring-beans/src/test/resources/beans.xml"); System.out.println("number : " + ctx.getBeanDefinitionCount()); ((Person) ctx.getBean("person")).work(); } }
進入FileSystemXmlApplicationContext類查看構造方法:
可以看到該構造方法被重載了,可以傳遞 configLocation 字符串或者字符串數組,也就是說,可以傳遞過個配置文件的地址。默認刷新為true,parent 容器為null。並進入另一個構造器:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); // parent為null this.setConfigLocations(configLocations); if (refresh) { this.refresh(); } }
2、設置資源加載器和資源定位
首先,調用父類容器的構造方法(super(parent)方法)為容器設置好Bean資源加載器。
然后,再調用父類AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法設置Bean定義資源文件的定位路徑。
通過追蹤FileSystemXmlApplicationContext的繼承體系,發現其父類的父類AbstractApplicationContext中初始化IoC容器所做的主要源碼如下:
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean { //靜態初始化塊,在整個容器創建過程中只執行一次 static { //為了避免應用程序在Weblogic8.1關閉時出現類加載異常加載問題,加載IoC容 //器關閉事件(ContextClosedEvent)類 ContextClosedEvent.class.getName(); } //FileSystemXmlApplicationContext調用父類構造方法調用的就是該方法 public AbstractApplicationContext(ApplicationContext parent) { this.parent = parent; this.resourcePatternResolver = getResourcePatternResolver(); } //獲取一個Spring Source的加載器用於讀入Spring Bean定義資源文件 protected ResourcePatternResolver getResourcePatternResolver() { // AbstractApplicationContext繼承DefaultResourceLoader,也是一個S //Spring資源加載器,其getResource(String location)方法用於載入資源 return new PathMatchingResourcePatternResolver(this); } }
AbstractApplicationContext構造方法中調用PathMatchingResourcePatternResolver的構造方法創建Spring資源加載器:
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) { Assert.notNull(resourceLoader, "ResourceLoader must not be null"); //設置Spring的資源加載器 this.resourceLoader = resourceLoader; }
在設置容器的資源加載器之后,接下來FileSystemXmlApplicationContet執行setConfigLocations方法通過調用其父類AbstractRefreshableConfigApplicationContext的方法進行對Bean定義資源文件的定位,該方法的源碼如下:
//處理單個資源文件路徑為一個字符串的情況 public void setConfigLocation(String location) { //String CONFIG_LOCATION_DELIMITERS = ",; /t/n"; //即多個資源文件路徑之間用” ,; /t/n”分隔,解析成數組形式 setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)); } //解析Bean定義資源文件的路徑,處理多個資源文件字符串數組 public void setConfigLocations(String[] locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { // resolvePath為同一個類中將字符串解析為路徑的方法 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
3、高級容器初始化入口
Spring IoC容器對Bean定義資源的載入是從refresh()函數開始的,refresh()是一個模板方法,refresh()方法的作用是:在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似於對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入
FileSystemXmlApplicationContext通過調用其父類AbstractApplicationContext的refresh()函數啟動整個IoC容器對Bean定義的載入過程:
/** * * 1. 構建Be按Factory,以便產生所需要的bean定義實例 * 2. 注冊可能感興趣的事件 * 3. 創建bean 實例對象 * 4. 觸發被監聽的事件 * */ @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 為刷新准備應用上下文 prepareRefresh(); // 告訴子類刷新內部bean工廠,即在子類中啟動refreshBeanFactory()的地方----創建bean工廠,根據配置文件生成bean定義 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 在這個上下文中使用bean工廠 prepareBeanFactory(beanFactory); try { // 設置BeanFactory的后置處理器 postProcessBeanFactory(beanFactory); // 調用BeanFactory的后處理器,這些后處理器是在Bean定義中向容器注冊的 invokeBeanFactoryPostProcessors(beanFactory); // 注冊Bean的后處理器,在Bean創建過程中調用 registerBeanPostProcessors(beanFactory); //對上下文的消息源進行初始化 initMessageSource(); // 初始化上下文中的事件機制 initApplicationEventMulticaster(); // 初始化其他的特殊Bean onRefresh(); // 檢查監聽Bean並且將這些Bean向容器注冊 registerListeners(); // 實例化所有的(non-lazy-init)單件 finishBeanFactoryInitialization(beanFactory); // 發布容器事件,結束refresh過程 finishRefresh(); }catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 為防止bean資源占用,在異常處理中,銷毀已經在前面過程中生成的單件bean destroyBeans(); // 重置“active”標志 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(); } } }
-
prepareRefresh - 准備上下文環境信息,為接下來ApplicationContext 容器的初始化流程做准備。
- BeanFactory 的構建。 BeanFactory 是 ApplicationContext 的父接口。是 spring 框架中的頂級容器工廠對象。BeanFactory 只能管理 bean 對象。沒有其他功能。如:aop 切面管理、propertyplaceholder 的加載等就不屬於BeanFactory的管理范圍。 構建 BeanFactory 的功能就是管理 bean 對象。創建 BeanFactory 中管理的 bean 對象。
- postProcessBeanFactory - 加載配置中 BeanFactory 無法處理的內容 。如:propertyplacehodler 的加載。
- invokeBeanFactoryPostProcessors- 將上一步加載的內容,作為一個容器可以管理的bean對象注冊到 ApplicationContext 中。底層實質是在將 postProcessBeanFactory 中加載的內容包裝成一個容器 ApplicationContext 可以管理的 bean 對象。
- registerBeanPostProcessors - 繼續完成上一步的注冊操作。配置文件中配置的 bean 對象都創建並注冊完成。
- initMessageSource - i18n,國際化。初始化國際化消息源。
- initApplicationEventMulticaster - 注冊事件多播監聽。如 ApplicationEvent 事件。是 spring 框架中的觀察者模式實現機制。
- onRefresh - 初始化主題資源(ThemeSource)。spring 框架提供的視圖主題信息。 registerListeners- 創建監聽器,並注冊。
- finishBeanFactoryInitialization - 初始化配置中出現的所有的 lazy-init=false 的 bean 對象,且 bean 對象必須是 singleton 的。
- finishRefresh - 最后一步。 發布最終事件。生命周期監聽事件。 spring 容器定義了生命周期接口。可以實現容器啟動調用初始化,容器銷毀之前調用回收資源。Lifecycle 接口。
refresh()方法主要為IoC容器Bean的生命周期管理提供條件,Spring IOC容器載入Bean定義資源文件從其子類容器的refreshBeanFactory()方法啟動,所以整個refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();” 這句以后代碼的都是注冊容器的信息源和生命周期事件,載入過程就是從這句代碼啟動。
refresh()方法的作用是:在創建IOC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IOC容器。refresh的作用類似於對IOC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。
4、XMLBeanFactory低級容器初始化入口
AbstractApplicationContext的obtainFreshBeanFactory()方法調用子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資源文件的過程,代碼如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //這里使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現調用子類容器的refreshBeanFactory()方法 refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
AbstractApplicationContext子類的refreshBeanFactory()方法只抽象定義了refreshBeanFactory()方法,容器真正調用的是其子類AbstractRefreshableApplicationContext實現的 refreshBeanFactory()方法,方法的源碼如下:
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) {//如果已經有容器,銷毀容器中的bean,關閉容器 destroyBeans(); closeBeanFactory(); } try { // 創建IoC容器 // new DefaultListableBeanFactory(getInternalParentBeanFactory()) DefaultListableBeanFactory beanFactory = createBeanFactory(); // 設置序列化 beanFactory.setSerializationId(getId()); // 對IoC容器進行定制化,如設置啟動參數,開啟注解的自動裝配等 customizeBeanFactory(beanFactory); // 調用載入Bean定義的方法,主要這里又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現調用子類容器 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
首先判斷是否存在了 BeanFactory,如果有則銷毀重新創建,調用 createBeanFactory 方法,該方法中就是像注釋寫的:創建了 DefaultListableBeanFactory ,也既是說,DefaultListableBeanFactory 就是 BeanFactory的默認實現。
接着調用loadBeanDefinitions(beanFactory)裝載bean的Definitions, Definition 是核心之一,代表着 IOC 中的基本數據結構。
5、調用loadBeanDefinitions載入Bean定義
該方法也是個抽象方法,默認實現是AbstractRefreshableApplicationContext的子類AbstractXmlApplicationContext ,繼續看該方法實現:
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext { //實現父類抽象的載入Bean定義方法 @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 創建XmlBeanDefinitionReader,即創建Bean讀取器,並通過回調設置到容器中去,容器使用該讀取器讀取Bean定義資源 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 為Bean讀取器設置Spring資源加載器,AbstractXmlApplicationContext的祖先父類AbstractApplicationContext繼承DefaultResourceLoader
// 因此,容器本身也是一個資源加載器 beanDefinitionReader.setResourceLoader(this); // 為Bean讀取器設置SAX xml解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 當Bean讀取器讀取Bean定義的Xml資源文件時,啟用Xml的校驗機制 initBeanDefinitionReader(beanDefinitionReader); // Bean讀取器真正實現加載的方法 loadBeanDefinitions(beanDefinitionReader); } // Xml Bean讀取器加載Bean定義資源 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 獲取Bean定義資源的定位 Resource[] configResources = getConfigResources(); if (configResources != null) { // Xml Bean讀取器調用其父類AbstractBeanDefinitionReader讀取定位的Bean定義資源 reader.loadBeanDefinitions(configResources); } // 如果子類中獲取的Bean定義資源定位為空,則獲取FileSystemXmlApplicationContext構造方法中setConfigLocations方法設置的資源 String[] configLocations = getConfigLocations(); if (configLocations != null) { // Xml Bean讀取器調用其父類AbstractBeanDefinitionReader讀取定位的Bean定義資源 reader.loadBeanDefinitions(configLocations); } } // 這里又使用了一個委托模式,調用子類的獲取Bean定義資源定位的方法,該方法在ClassPathXmlApplicationContext中進行實現 // 對於我們舉例分析源碼的FileSystemXmlApplicationContext沒有使用該方法 protected Resource[] getConfigResources() { return null; } }
Xml Bean讀取器(XmlBeanDefinitionReader)調用其父類AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法讀取Bean定義資源。由於我們使用FileSystemXmlApplicationContext作為例子分析,因此getConfigResources的返回值為null,因此程序跳過第一個if,進入第二個if執行reader.loadBeanDefinitions(configLocations)分支。
6、AbstractBeanDefinitionReader讀取Bean定義資源
XmlBeanDefinitionReader在其抽象父類AbstractBeanDefinitionReader中定義了載入過程,loadBeanDefinitions方法源碼如下(注意方法入參是location):
//重載方法,調用下面的loadBeanDefinitions(String, Set<Resource>);方法 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } //重載方法,調用loadBeanDefinitions(String); public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; for (String location : locations) { counter += loadBeanDefinitions(location); } return counter; } public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { //獲取在IoC容器初始化過程中設置的資源加載器 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { try { //將指定位置的Bean定義資源文件解析為Spring IoC容器封裝的資源 //加載多個指定位置的Bean定義資源文件 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能 int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { //將指定位置的Bean定義資源文件解析為Spring IoC容器封裝的資源 //加載單個指定位置的Bean定義資源文件 Resource resource = resourceLoader.getResource(location); //委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能 int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
loadBeanDefinitions(Resource...resources)方法和上面分析的3個方法類似,同樣也是調用XmlBeanDefinitionReader的loadBeanDefinitions方法。如代碼:
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; Resource[] var3 = resources; int var4 = resources.length; for(int var5 = 0; var5 < var4; ++var5) { Resource resource = var3[var5]; counter += this.loadBeanDefinitions((Resource)resource); } return counter; }
從對AbstractBeanDefinitionReader的loadBeanDefinitions方法源碼分析可以看出該方法做了以下兩件事:
首先,調用資源加載器的獲取資源方法resourceLoader.getResource(location),獲取到要加載的資源。
其次,真正執行加載功能是其子類XmlBeanDefinitionReader的loadBeanDefinitions方法。
結合ResourceLoader與ApplicationContext的繼承關系圖,可以知道此時調用的是DefaultResourceLoader中的getSource()方法定位Resource,因為FileSystemXmlApplicationContext本身就是DefaultResourceLoader的實現類,所以此時又回到了FileSystemXmlApplicationContext中來。
7、資源加載器獲取要讀入的資源
XmlBeanDefinitionReader通過調用其父類DefaultResourceLoader的getResource方法獲取要加載的資源,其源碼如下:
//獲取Resource的具體實現方法 public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //如果是類路徑的方式,那需要使用ClassPathResource 來得到bean 文件的資源對象 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } try { // 如果是URL 方式,使用UrlResource 作為bean 文件的資源對象 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { } //如果既不是classpath標識,又不是URL標識的Resource定位,則調用 //容器本身的getResourceByPath方法獲取Resource return getResourceByPath(location); }
FileSystemXmlApplicationContext容器提供了getResourceByPath方法的實現,就是為了處理既不是classpath標識,又不是URL標識的Resource定位這種情況。
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } //這里使用文件系統資源對象來定義bean 文件 return new FileSystemResource(path); }
這樣代碼就回到了 FileSystemXmlApplicationContext 中來,他提供了FileSystemResource 來完成從文件系統得到配置文件的資源定義。
這樣,就可以從文件系統路徑上對IOC 配置文件進行加載 - 當然我們可以按照這個邏輯從任何地方加載,在Spring 中我們看到它提供 的各種資源抽象,比如ClassPathResource, URLResource,FileSystemResource 等來供我們使用。上面我們看到的是定位Resource 的一個過程,而這只是加載過程的一部分。
8、XmlBeanDefinitionReader加載Bean定義資源
Bean定義的Resource得到了后,繼續回到XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean文件的資源定義以后的載入過程。
// XmlBeanDefinitionReader加載資源的入口方法 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { // 將讀入的XML資源進行特殊編碼處理 return loadBeanDefinitions(new EncodedResource(resource)); } // 這里是載入XML形式Bean定義資源文件方法 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
... ... try { // 將資源文件轉為InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { // 從InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 這里是具體的讀取過程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { // 關閉從Resource中得到的IO流 inputStream.close(); }
... ... } }
// 從特定XML文件中實際載入Bean定義資源的方法 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 將XML文件轉換為DOM對象,解析過程由documentLoader實現 Document doc = this.doLoadDocument(inputSource, resource); // 這里是啟動對Bean定義解析的詳細過程,該解析過程會用到Spring的Bean配置規則 return this.registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException var4) { throw var4; } catch (SAXParseException var5) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5); } catch (SAXException var6) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6); } catch (ParserConfigurationException var7) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7); } catch (IOException var8) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8); } catch (Throwable var9) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9); } }
通過源碼分析,載入Bean定義資源文件的最后一步是將Bean定義資源轉換為Document對象,該過程由documentLoader實現。
9、DocumentLoader將Bean定義資源轉換為Document對象
DocumentLoader通過其實現類DefaultDocumentLoader將Bean定義資源轉換成Document對象,源碼如下:
//使用標准的JAXP將載入的Bean定義資源轉換成document對象 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //創建文件解析器工廠 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } //創建文檔解析器 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); //解析Spring的Bean定義資源 return builder.parse(inputSource); } protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException { //創建文檔解析工廠 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); //設置解析XML的校驗 if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) { factory.setValidating(true); if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) { factory.setNamespaceAware(true); try { factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE); } catch (IllegalArgumentException ex) { ParserConfigurationException pcex = new ParserConfigurationException( "Unable to validate using XSD: Your JAXP provider [" + factory + "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " + "Upgrade to Apache Xerces (or Java 1.5) for full XSD support."); pcex.initCause(ex); throw pcex; } } } return factory; }
該解析過程調用JavaEE標准的JAXP標准進行處理。至此Spring IOC容器根據定位的Bean定義資源文件,將其加載讀入並轉換成為Document對象過程完成。接下來我們要繼續分析Spring IOC容器將載入的Bean定義資源文件轉換為Document對象之后,是如何將其解析為Spring IOC管理的Bean對象並將其注冊到容器中的。
10、XmlBeanDefinitionReader解析載入的Bean定義資源文件
XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML文件中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之后將其轉換為Document對象,接下來調用registerBeanDefinitions啟動Spring IOC容器對Bean定義的解析過程,registerBeanDefinitions方法源碼如下:
//按照Spring的Bean語義要求將Bean定義資源解析並轉換為容器內部數據結構 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //獲得容器中注冊的Bean數量 int countBefore = getRegistry().getBeanDefinitionCount(); //解析過程入口,這里使用了委派模式,BeanDefinitionDocumentReader只是個接口,//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //統計解析的Bean數量 return getRegistry().getBeanDefinitionCount() - countBefore; } //創建BeanDefinitionDocumentReader對象,解析Document對象 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); }
Bean定義資源的載入解析分為以下兩個過程:
首先,通過調用XML解析器將Bean定義資源文件轉換得到Document對象,但是這些Document對象並沒有按照Spring的Bean規則進行解析。這一步是載入的過程
其次,在完成通用的XML解析之后,按照Spring的Bean規則對Document對象進行解析。
按照Spring的Bean規則對Document對象解析的過程是在接口BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。
11、DefaultBeanDefinitionDocumentReader對Bean定義的Document對象解析
BeanDefinitionDocumentReader接口通過registerBeanDefinitions方法調用其實現類DefaultBeanDefinitionDocumentReader對Document對象進行解析,解析的代碼如下:
//根據Spring DTD對Bean的定義規則解析Bean定義Document對象 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //獲得XML描述符 this.readerContext = readerContext; logger.debug("Loading bean definitions"); //獲得Document的根元素 Element root = doc.getDocumentElement(); //具體的解析過程由BeanDefinitionParserDelegate實現, //BeanDefinitionParserDelegate中定義了Spring Bean定義XML文件的各種元素 BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); //在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴展性 preProcessXml(root); //從Document的根元素開始進行Bean定義的Document對象 parseBeanDefinitions(root, delegate); //在解析Bean定義之后,進行自定義的解析,增加解析過程的可擴展性 postProcessXml(root); } //創建BeanDefinitionParserDelegate,用於完成真正的解析過程 protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) { BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); //BeanDefinitionParserDelegate初始化Document根元素 delegate.initDefaults(root); return delegate; } //使用Spring的Bean規則從Document的根元素開始進行Bean定義的Document對象 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //Bean定義的Document對象使用了Spring默認的XML命名空間 if (delegate.isDefaultNamespace(root)) { //獲取Bean定義的Document對象根元素的所有子節點 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //獲得Document節點是XML元素節點 if (node instanceof Element) { Element ele = (Element) node; //Bean定義的Document的元素節點使用的是Spring默認的XML命名空間 if (delegate.isDefaultNamespace(ele)) { //使用Spring的Bean規則解析元素節點 parseDefaultElement(ele, delegate); } else { //沒有使用Spring默認的XML命名空間,則使用用戶自定義的解//析規則解析元素節點 delegate.parseCustomElement(ele); } } } } else { //Document的根節點沒有使用Spring默認的命名空間,則使用用戶自定義的 //解析規則解析Document根節點 delegate.parseCustomElement(root); } } //使用Spring的Bean規則解析Document元素節點 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果元素節點是<Import>導入元素,進行導入解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //如果元素節點是<Alias>別名元素,進行別名解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //元素節點既不是導入元素,也不是別名元素,即普通的<Bean>元素, //按照Spring的Bean規則解析元素 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } } //解析<Import>導入元素,從給定的導入路徑加載Bean定義資源到Spring IoC容器中 protected void importBeanDefinitionResource(Element ele) { //獲取給定的導入元素的location屬性 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); //如果導入元素的location屬性值為空,則沒有導入任何資源,直接返回 if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } //使用系統變量值解析location屬性值 location = SystemPropertyUtils.resolvePlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<Resource>(4); //標識給定的導入元素的location是否是絕對路徑 boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { //給定的導入元素的location不是絕對路徑 } //給定的導入元素的location是絕對路徑 if (absoluteLocation) { try { //使用資源讀入器加載給定路徑的Bean定義資源 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } } else { //給定的導入元素的location是相對路徑 try { int importCount; //將給定導入元素的location封裝為相對路徑資源 Resource relativeResource = getReaderContext().getResource().createRelative(location); //封裝的相對路徑資源存在 if (relativeResource.exists()) { //使用資源讀入器加載Bean定義資源 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } //封裝的相對路徑資源不存在 else { //獲取Spring IoC容器資源讀入器的基本路徑 String baseLocation = getReaderContext().getResource().getURL().toString(); //根據Spring IoC容器資源讀入器的基本路徑加載給定導入 //路徑的資源 importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); //在解析完<Import>元素之后,發送容器導入其他資源處理完成事件 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); } //解析<Alias>別名元素,為Bean向Spring IoC容器注冊別名 protected void processAliasRegistration(Element ele) { //獲取<Alias>別名元素中name的屬性值 String name = ele.getAttribute(NAME_ATTRIBUTE); //獲取<Alias>別名元素中alias的屬性值 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; //<alias>別名元素的name屬性值為空 if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } //<alias>別名元素的alias屬性值為空 if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { //向容器的資源讀入器注冊別名 getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } //在解析完<Alias>元素之后,發送容器別名處理完成事件 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } } //解析Bean定義資源Document對象的普通元素 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類 //對Document對象中<Bean>元素的解析由BeanDefinitionParserDelegate實現 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //向Spring IoC容器注冊解析得到的Bean定義,這是Bean定義向IoC容器注冊的入口 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } //在完成向Spring IoC容器注冊解析得到的Bean定義之后,發送注冊事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
通過上述Spring IOC容器對載入的Bean定義Document解析可以看出,我們使用Spring時,在Spring配置文件中可以使用<Import>元素來導入IOC容器所需要的其他資源,Spring IOC容器在解析時會首先將指定導入的資源加載進容器中。使用<Ailas>別名時,Spring IoC容器首先將別名元素所定義的別名注冊到容器中。
對於既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置文件中普通的<Bean>元素的解析由BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法來實現。
12、BeanDefinitionParserDelegate解析Bean定義資源文件中的<Bean>元素
Bean定義資源文件中的<Import>和<Alias>元素解析在DefaultBeanDefinitionDocumentReader中已經完成,對Bean定義資源文件中使用最多的<Bean>元素交由BeanDefinitionParserDelegate來解析,其解析實現的源碼如下:
//解析<Bean>元素的入口 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } //解析Bean定義資源文件中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name //和別名屬性 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //獲取<Bean>元素中的id屬性值 String id = ele.getAttribute(ID_ATTRIBUTE); //獲取<Bean>元素中的name屬性值 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); ////獲取<Bean>元素中的alias屬性值 List<String> aliases = new ArrayList<String>(); //將<Bean>元素中的所有name屬性值存放到別名中 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; //如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } //檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean> //元素中是否包含子<Bean>元素 if (containingBean == null) { //檢查<Bean>元素所配置的id、name或者別名是否重復 checkNameUniqueness(beanName, aliases, ele); } //詳細對<Bean>元素中配置的Bean定義進行解析的地方 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { //如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並注冊 beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { //如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器注冊 beanName = this.readerContext.generateBeanName(beanDefinition); //為解析的Bean使用別名注冊時,為了向后兼容 //Spring1.2/2.0,給別名添加類名后綴 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } //當解析出錯時,返回null return null; } //詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性數據 public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { //記錄解析的<Bean> this.parseState.push(new BeanEntry(beanName)); //這里只讀取<Bean>元素中配置的class名字,然后載入到BeanDefinition中去 //只是記錄配置的class名字,不做實例化,對象的實例化在依賴注入時完成 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; //如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } //根據<Bean>元素配置的class名稱和parent屬性值創建BeanDefinition //為載入Bean定義信息做准備 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //對當前的<Bean>元素中配置的一些屬性進行解析和設置,如配置的單態(singleton)屬性等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //為<Bean>元素解析的Bean設置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //對<Bean>元素的meta(元信息)屬性解析 parseMetaElements(ele, bd); //對<Bean>元素的lookup-method屬性解析 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //對<Bean>元素的replaced-method屬性解析 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析<Bean>元素的構造方法設置 parseConstructorArgElements(ele, bd); //解析<Bean>元素的<property>設置 parsePropertyElements(ele, bd); //解析<Bean>元素的qualifier屬性 parseQualifierElements(ele, bd); //為當前解析的Bean設置所需的資源和依賴對象 bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } //解析<Bean>元素出錯時,返回null return null; }
只要使用過Spring,對Spring配置文件比較熟悉的人,通過對上述源碼的分析,就會明白我們在Spring配置文件中<Bean>元素的中配置的屬性就是通過該方法解析和設置到Bean中去的。
注意:在解析<Bean>元素過程中沒有創建和實例化Bean對象,只是創建了Bean對象的定義類BeanDefinition,將<Bean>元素中的配置信息設置到BeanDefinition中作為記錄,當依賴注入時才使用這些記錄信息創建和實例化具體的Bean對象。
上面方法中一些對一些配置如元信息(meta)、qualifier等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的<Bean>元素時,配置最多的是<property>屬性,因此我們下面繼續分析源碼,了解Bean的屬性在解析時是如何設置的。
13、BeanDefinitionParserDelegate解析<property>元素
BeanDefinitionParserDelegate在解析<Bean>調用parsePropertyElements方法解析<Bean>元素中的<property>屬性子元素,解析源碼如下:
//解析<Bean>元素中的<property>子元素 public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //獲取<Bean>元素中所有的子元素 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //如果子元素是<property>子元素,則調用解析<property>子元素方法解析 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { parsePropertyElement((Element) node, bd); } } } //解析<property>元素 public void parsePropertyElement(Element ele, BeanDefinition bd) { //獲取<property>元素的名字 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { //如果一個Bean中已經有同名的property存在,則不進行解析,直接返回。 //即如果在同一個Bean中配置同名的property,則只有第一個起作用 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } //解析獲取property的值 Object val = parsePropertyValue(ele, bd, propertyName); //根據property的名字和值創建property實例 PropertyValue pv = new PropertyValue(propertyName, val); //解析<property>元素中的屬性 parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } } //解析獲取property值 public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; //獲取<property>的所有子元素,只能是其中一種類型:ref,value,list等 NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //子元素不是description和meta屬性 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } else {//當前<property>元素包含有子元素 subElement = (Element) node; } } } //判斷property的屬性值是ref還是value,不允許既是ref又是value boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } //如果屬性是ref,創建一個ref的數據對象RuntimeBeanReference,這個對象 //封裝了ref信息 if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } //一個指向運行時所依賴對象的引用 RuntimeBeanReference ref = new RuntimeBeanReference(refName); //設置這個ref的數據對象是被當前的property對象所引用 ref.setSource(extractSource(ele)); return ref; } //如果屬性是value,創建一個value的數據對象TypedStringValue,這個對象 //封裝了value信息 else if (hasValueAttribute) { //一個持有String類型值的對象 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); //設置這個value數據對象是被當前的property對象所引用 valueHolder.setSource(extractSource(ele)); return valueHolder; } //如果當前<property>元素還有子元素 else if (subElement != null) { //解析<property>的子元素 return parsePropertySubElement(subElement, bd); } else { //propery屬性中既不是ref,也不是value屬性,解析出錯返回null error(elementName + " must specify a ref or value", ele); return null; } }
通過對上述源碼的分析,我們可以了解在Spring配置文件中,<Bean>元素中<property>元素的相關配置是如何處理的:
- ref:被封裝為指向依賴對象一個引用。
- value:配置都會封裝成一個字符串類型的對象。
- ref和value都通過“解析的數據類型屬性值.setSource(extractSource(ele));”方法將屬性值/引用與所引用的屬性關聯起來。
在方法的最后對於<property>元素的子元素通過parsePropertySubElement 方法解析。
14、解析<property>元素的子元素
在BeanDefinitionParserDelegate類中的parsePropertySubElement方法對<property>中的子元素解析,源碼如下:
//解析<property>元素中ref,value或者集合等子元素 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { //如果<property>沒有使用Spring默認的命名空間,則使用用戶自定義的規則解析//內嵌元素 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } //如果子元素是bean,則使用解析<Bean>元素的方法解析 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } //如果子元素是ref,ref中只能有以下3個屬性:bean、local、parent else if (nodeNameEquals(ele, REF_ELEMENT)) { //獲取<property>元素中的bean屬性值,引用其他解析的Bean的名稱 //可以不再同一個Spring配置文件中,具體請參考Spring對ref的配置規則 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { //獲取<property>元素中的local屬性值,引用同一個Xml文件中配置 //的Bean的id,local和ref不同,local只能引用同一個配置文件中的Bean refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (!StringUtils.hasLength(refName)) { //獲取<property>元素中parent屬性值,引用父級容器中的Bean refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean', 'local' or 'parent' is required for <ref> element", ele); return null; } } } //沒有配置ref的目標屬性值 if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } //創建ref類型數據,指向被引用的對象 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); //設置引用類型值是被當前子元素所引用 ref.setSource(extractSource(ele)); return ref; } //如果子元素是<idref>,使用解析ref元素的方法解析 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } //如果子元素是<value>,使用解析value元素的方法解析 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } //如果子元素是null,為<property>設置一個封裝null值的字符串數據 else if (nodeNameEquals(ele, NULL_ELEMENT)) { TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } //如果子元素是<array>,使用解析array集合子元素的方法解析 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } //如果子元素是<list>,使用解析list集合子元素的方法解析 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } //如果子元素是<set>,使用解析set集合子元素的方法解析 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } //如果子元素是<map>,使用解析map集合子元素的方法解析 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } //如果子元素是<props>,使用解析props集合子元素的方法解析 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } //既不是ref,又不是value,也不是集合,則子元素配置錯誤,返回null else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
通過上述源碼分析,我們明白了在Spring配置文件中,對<property>元素中配置的Array、List、Set、Map、Prop等各種集合子元素的都通過上述方法解析,生成對應的數據對象,比如ManagedList、ManagedArray、ManagedSet等,這些Managed類是Spring對象BeanDefiniton的數據封裝,對集合數據類型的具體解析有各自的解析方法實現,解析方法的命名非常規范,一目了然,我們對<list>集合元素的解析方法進行源碼分析,了解其實現過程。
15、解析<list>子元素
在BeanDefinitionParserDelegate類中的parseListElement方法就是具體實現解析<property>元素中的<list>集合子元素,源碼如下:
//解析<list>集合子元素 public List parseListElement(Element collectionEle, BeanDefinition bd) { //獲取<list>元素中的value-type屬性,即獲取集合元素的數據類型 String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //獲取<list>集合元素中的所有子節點 NodeList nl = collectionEle.getChildNodes(); //Spring中將List封裝為ManagedList ManagedList<Object> target = new ManagedList<Object>(nl.getLength()); target.setSource(extractSource(collectionEle)); //設置集合目標數據類型 target.setElementTypeName(defaultElementType); target.setMergeEnabled(parseMergeAttribute(collectionEle)); //具體的<list>元素解析 parseCollectionElements(nl, target, bd, defaultElementType); return target; } //具體解析<list>集合元素,<array>、<list>和<set>都使用該方法解析 protected void parseCollectionElements( NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) { //遍歷集合所有節點 for (int i = 0; i < elementNodes.getLength(); i++) { Node node = elementNodes.item(i); //節點不是description節點 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) { //將解析的元素加入集合中,遞歸調用下一個子元素 target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); } } }
經過對Spring Bean定義資源文件轉換的Document對象中的元素層層解析,Spring IOC現在已經將XML形式定義的Bean定義資源文件轉換為Spring IOC所識別的數據結構——BeanDefinition,它是Bean定義資源文件中配置的POJO對象在Spring IoC容器中的映射,我們可以通過AbstractBeanDefinition為入口,供IOC容器進行索引、查詢和操作。
通過Spring IOC容器對Bean定義資源的解析后,IOC容器大致完成了管理Bean對象的准備工作,即初始化過程,但是最為重要的依賴注入還沒有發生,現在在IOC容器中BeanDefinition存儲的只是一些靜態信息,接下來需要向容器注冊Bean定義信息才能全部完成IOC容器的初始化過程。
16、解析過后的BeanDefinition在IOC容器中的注冊
接下來會到我們第3步中分析DefaultBeanDefinitionDocumentReader對Bean定義轉換的Document對象解析的流程中,在其parseDefaultElement方法中完成對Document對象的解析后得到封裝BeanDefinition的BeanDefinitionHold對象,然后調用BeanDefinitionReaderUtils的registerBeanDefinition方法向IOC容器注冊解析的Bean,BeanDefinitionReaderUtils的注冊的源碼如下:
//將解析的BeanDefinitionHold注冊到容器中 public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //獲取解析的BeanDefinition的名稱 String beanName = definitionHolder.getBeanName(); //向IoC容器注冊BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //如果解析的BeanDefinition有別名,向容器為其注冊別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
當調用BeanDefinitionReaderUtils向IOC容器注冊解析的BeanDefinition時,真正完成注冊功能的是DefaultListableBeanFactory。
17、DefaultListableBeanFactory向IOC容器注冊解析后的BeanDefinition
DefaultListableBeanFactory中使用一個ConcurrentHashMap<String, BeanDefinition>的集合對象存放IoC容器中注冊解析的BeanDefinition,向IOC容器注冊的主要源碼如下:
//存儲注冊的俄BeanDefinition private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); //向IoC容器注冊解析的BeanDefiniton public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //校驗解析的BeanDefiniton if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //注冊的過程中需要線程同步,以保證數據的一致性 synchronized (this.beanDefinitionMap) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); //檢查是否有同名的BeanDefinition已經在IoC容器中注冊,如果已經注冊, //並且不允許覆蓋已注冊的Bean,則拋出注冊失敗異常 if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else {//如果允許覆蓋,則同名的Bean,后注冊的覆蓋先注冊的 if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } //IoC容器中沒有已經注冊同名的Bean,按正常注冊流程注冊 else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } this.beanDefinitionMap.put(beanName, beanDefinition); //重置所有已經注冊過的BeanDefinition的緩存 resetBeanDefinition(beanName); } }
至此,Bean定義資源文件中配置的Bean被解析過后,已經注冊到IOC容器中,被容器管理起來,真正完成了IOC容器初始化所做的全部工作。現 在IOC容器中已經建立了整個Bean的配置信息,這些BeanDefinition信息已經可以使用,並且可以被檢索,IOC容器的作用就是對這些注冊的Bean定義信息進行處理和維護。這些的注冊的Bean定義信息是IOC容器控制反轉的基礎,正是有了這些注冊的數據,容器才可以進行依賴注入。
總結
- 初始化的入口在容器實現中的 refresh()調用來完成
- 對 bean 定義載入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致過程如下:通過 ResourceLoader 來完成資源文件位置的定位,DefaultResourceLoader 是默認的實現,同時上下文本身就給出了 ResourceLoader 的實現,可以從類路徑,文件系統, URL 等方式來定為資源位置。如果是 XmlBeanFactory作為 IOC 容器,那么需要為它指定 bean 定義的資源,也就是說 bean 定義文件時通過抽象成 Resource 來被 IOC 容器處理的,容器通過 BeanDefinitionReader來完成定義信息的解析和 Bean 信息的注冊,往往使用的是XmlBeanDefinitionReader 來解析 bean 的 xml 定義文件 - 實際的處理過程是委托給 BeanDefinitionParserDelegate 來完成的,從而得到 bean 的定義信息,這些信息在 Spring 中使用 BeanDefinition 對象來表示 - 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition 這些相關的方法 - 他們都是為處理 BeanDefinitin 服務的, 容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注冊,這由 IOC 實現 BeanDefinitionRegistry 接口來實現。注冊過程就是在 IOC 容器內部維護的一個ConcurrentHashMap 來保存得到的 BeanDefinition 的過程。這個 ConcurrentHashMap 是 IOC 容器持有 bean 信息的場所,以后對 bean 的操作都是圍繞這個ConcurrentHashMap 來實現的.
- 然后我們就可以通過 BeanFactory 和 ApplicationContext 來享受到 Spring IOC 的服務了,在使用 IOC 容器的時候,我們注意到除了少量粘合代碼,絕大多數以正確 IoC 風格編寫的應用程序代碼完全不用關心如何到達工廠,因為容器將把這些對象與容器管理的其他對象鈎在一起。基本的策略是把工廠放到已知的地方,最好是放在對預期使用的上下文有意義的地方,以及代碼將實際需要訪問工廠的地方。 Spring 本身提供了對聲明式載入 web 應用程序用法的應用程序上下文,並將其存儲在ServletContext 中的框架實現。具體可以參見以后的文章
在使用 Spring IOC 容器的時候我們還需要區別兩個概念:
Beanfactory 和 Factory bean,其中 BeanFactory 指的是 IOC 容器的編程抽象,比如 ApplicationContext, XmlBeanFactory 等,這些都是 IOC 容器的具體表現,需要使用什么樣的容器由客戶決定,但 Spring 為我們提供了豐富的選擇。 FactoryBean 只是一個可以在 IOC容器中被管理的一個 bean,是對各種處理過程和資源使用的抽象,Factory bean 在需要時產生另一個對象,而不返回 FactoryBean本身,我們可以把它看成是一個抽象工廠,對它的調用返回的是工廠生產的產品。所有的 Factory bean 都實現特殊的org.springframework.beans.factory.FactoryBean 接口,當使用容器中 factory bean 的時候,該容器不會返回 factory bean 本身,而是返回其生成的對象。Spring 包括了大部分的通用資源和服務訪問抽象的 Factory bean 的實現,其中包括:對 JNDI 查詢的處理、對代理對象的處理、對事務性代理的處理、對 RMI 代理的處理等,這些我們都可以看成是具體的工廠,看成是Spring 為我們建立好的工廠。也就是說 Spring 通過使用抽象工廠模式為我們准備了一系列工廠來生產一些特定的對象,免除我們手工重復的工作,我們要使用時只需要在 IOC 容器里配置好就能很方便的使用了。
五、IOC容器的依賴注入
1、依賴注入發生的時間
當Spring IoC容器完成了Bean定義資源的定位、載入、解析和注冊以后,IOC容器中已經管理類Bean定義的相關數據,但是此時IoC容器還沒有對所管理的Bean進行依賴注入,依賴注入在以下兩種情況發生:
- 用戶第一次通過getBean方法向IOC容索要Bean時,IOC容器觸發依賴注入。
- 當用戶在Bean定義資源中為<Bean>元素配置了lazy-init屬性,即讓容器在解析注冊Bean定義時進行預實例化,觸發依賴注入。
BeanFactory接口定義了Spring IOC容器的基本功能規范,是Spring IOC容器所應遵守的最底層和最基本的編程規范。BeanFactory接口中定義了幾個getBean方法,就是用戶向IOC容器索取管理的Bean的方法,我們通過分析其子類的具體實現,理解Spring IOC容器在用戶索取Bean時如何完成依賴注入。
在BeanFactory中我們看到getBean(String…)函數,它的具體實現在AbstractBeanFactory中
2、AbstractBeanFactory通過getBean向IOC容器獲取被管理的Bean
//獲取IoC容器中指定名稱的Bean public Object getBean(String name) throws BeansException { //doGetBean才是真正向IoC容器獲取被管理Bean的過程 return doGetBean(name, null, null, false); } //獲取IoC容器中指定名稱和類型的Bean public <T> T getBean(String name, Class<T> requiredType) throws BeansException { //doGetBean才是真正向IoC容器獲取被管理Bean的過程 return doGetBean(name, requiredType, null, false); } //獲取IoC容器中指定名稱和參數的Bean public Object getBean(String name, Object... args) throws BeansException { //doGetBean才是真正向IoC容器獲取被管理Bean的過程 return doGetBean(name, null, args, false); } //獲取IoC容器中指定名稱、類型和參數的Bean public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException { //doGetBean才是真正向IoC容器獲取被管理Bean的過程 return doGetBean(name, requiredType, args, false); } //真正實現向IoC容器獲取Bean的功能,也是觸發依賴注入功能的地方 @SuppressWarnings("unchecked") protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { //根據指定的名稱獲取被管理Bean的名稱,剝離指定名稱中對容器的相關依賴,如果指定的是別名,將別名轉換為規范的Bean名稱 final String beanName = transformedBeanName(name); Object bean; //先從緩存中取是否已經有被創建過的單態類型的Bean,對於單態模式的Bean整個IoC容器中只創建一次,不需要重復創建 Object sharedInstance = getSingleton(beanName); //IOC容器創建單態模式Bean實例對象 if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { //如果指定名稱的Bean在容器中已有單態模式的Bean被創建,直接返回已經創建的Bean if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } //獲取給定Bean的實例對象,主要是完成FactoryBean的相關處理 //注意:BeanFactory是管理容器中Bean的工廠,而FactoryBean是創建創建對象的工廠Bean,兩者之間有區別 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else {
//緩存沒有正在創建的單態模式Bean //緩存中已經有已經創建的原型模式Bean,但是由於循環引用的問題導致實例化對象失敗 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } //對IoC容器中是否存在指定名稱的BeanDefinition進行檢查,首先檢查是否能在當前的BeanFactory中獲取的所需要的Bean
//如果不能則委托當前容器的父級容器去查找,如果還是找不到則沿着容器的繼承體系向父級容器查找 BeanFactory parentBeanFactory = getParentBeanFactory(); //當前容器的父級容器存在,且當前容器中不存在指定名稱的Bean if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { //解析指定Bean名稱的原始名稱 String nameToLookup = originalBeanName(name); if (args != null) { //委派父級容器根據指定名稱和顯式的參數查找 return (T) parentBeanFactory.getBean(nameToLookup, args); } else { //委派父級容器根據指定名稱和類型查找 return parentBeanFactory.getBean(nameToLookup, requiredType); } } //創建的Bean是否需要進行類型驗證,一般不需要 if (!typeCheckOnly) { //向容器標記指定的Bean已經被創建 markBeanAsCreated(beanName); } //根據指定Bean名稱獲取其父級的Bean定義,主要解決Bean繼承時子類合並父類公共屬性問題 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); //獲取當前Bean所有依賴Bean的名稱 String[] dependsOn = mbd.getDependsOn(); //如果當前Bean有依賴Bean if (dependsOn != null) { for (String dependsOnBean : dependsOn) { //遞歸調用getBean方法,獲取當前Bean的依賴Bean getBean(dependsOnBean); //把被依賴Bean注冊給當前依賴的Bean registerDependentBean(dependsOnBean, beanName); } } //創建單態模式Bean的實例對象 if (mbd.isSingleton()) { //這里使用了一個匿名內部類,創建Bean實例對象,並且注冊給所依賴的對象 sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { //創建一個指定Bean實例對象,如果有父級繼承,則合並子//類和父類的定義 return createBean(beanName, mbd, args); } catch (BeansException ex) { //顯式地從容器單態模式Bean緩存中清除實例對象 destroySingleton(beanName); throw ex; } } }); //獲取給定Bean的實例對象 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } //IoC容器創建原型模式Bean實例對象 else if (mbd.isPrototype()) { //原型模式(Prototype)是每次都會創建一個新的對象 Object prototypeInstance = null; try { //回調beforePrototypeCreation方法,默認的功能是注冊當前創//建的原型對象 beforePrototypeCreation(beanName); //創建指定Bean對象實例 prototypeInstance = createBean(beanName, mbd, args); } finally { //回調afterPrototypeCreation方法,默認的功能告訴IoC容器指//定Bean的原型對象不再創建了 afterPrototypeCreation(beanName); } //獲取給定Bean的實例對象 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } //要創建的Bean既不是單態模式,也不是原型模式,則根據Bean定義資源中配置的生命周期范圍,選擇實例化Bean的合適方法,這種在Web應用程序中比較常用 //如:request、session、application等生命周期 else { String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); //Bean定義資源中沒有配置生命周期范圍,則Bean定義不合法 if (scope == null) { throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'"); } try { //這里又使用了一個匿名內部類,獲取一個指定生命周期范圍的實例 Object scopedInstance = scope.get(beanName, new ObjectFactory() { public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); //獲取給定Bean的實例對象 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); } } } //對創建的Bean實例對象進行類型檢查 if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return (T) bean; }
通過上面對向IoC容器獲取Bean方法的分析,我們可以看到在Spring中,如果Bean定義的單例模式(Singleton),則容器在創建之前先從緩存中查找,以確保整個容器中只存在一個實例對象。如果Bean定義的是原型模式(Prototype),則容器每次都會創建一個新的實例對象。除此之外,Bean定義還可以擴展為指定其生命周期范圍。
上面的源碼只是定義了根據Bean定義的模式,采取的不同創建Bean實例對象的策略,具體的Bean實例對象的創建過程由實現了ObejctFactory接口的匿名內部類的createBean方法完成,ObejctFactory使用委派模式,具體的Bean實例創建過程交由其實現類AbstractAutowireCapableBeanFactory完成,我們繼續分析AbstractAutowireCapableBeanFactory的createBean方法的源碼,理解其創建Bean實例的具體實現過程。
3、AbstractAutowireCapableBeanFactory創建Bean實例對象
AbstractAutowireCapableBeanFactory類實現了ObejctFactory接口,創建容器指定的Bean實例對象,同時還對創建的Bean實例對象進行初始化處理。其創建Bean實例對象的方法源碼如下:
//創建Bean實例對象 protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } //判斷需要創建的Bean是否可以實例化,即是否可以通過當前的類加載器加載 resolveBeanClass(mbd, beanName); //校驗和准備Bean中的方法覆蓋 try { mbd.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { //如果Bean配置了初始化前和初始化后的處理器,則試圖返回一個需要創建//Bean的代理對象 Object bean = resolveBeforeInstantiation(beanName, mbd); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } //創建Bean的入口 Object beanInstance = doCreateBean(beanName, mbd, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } //真正創建Bean的方法 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { //封裝被創建的Bean對象 BeanWrapper instanceWrapper = null; if (mbd.isSingleton()){//單態模式的Bean,先從容器中緩存中獲取同名Bean instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //創建實例對象 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); //獲取實例化對象的類型 Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); //調用PostProcessor后置處理器 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references //向容器中緩存單態模式的Bean對象,以防循環引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } //這里是一個匿名內部類,為了防止循環引用,盡早持有對象的引用 addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } //Bean對象的初始化,依賴注入在此觸發 //這個exposedObject在初始化完成之后返回作為依賴注入完成后的Bean Object exposedObject = bean; try { //將Bean實例對象封裝,並且Bean定義中配置的屬性值賦值給實例對象 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { //初始化Bean對象 exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { //獲取指定名稱的已注冊的單態模式Bean對象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //根據名稱獲取的以注冊的Bean和正在實例化的Bean是同一個 if (exposedObject == bean) { //當前實例化的Bean初始化完成 exposedObject = earlySingletonReference; } //當前Bean依賴其他Bean,並且當發生循環引用時不允許新創建實例對象 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); //獲取當前Bean所依賴的其他Bean for (String dependentBean : dependentBeans) { //對依賴Bean進行類型檢查 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } //注冊完成依賴注入的Bean try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
通過對方法源碼的分析,我們看到具體的依賴注入實現在以下兩個方法中:
- createBeanInstance:生成Bean所包含的java對象實例。
- populateBean :對Bean屬性的依賴注入進行處理。
下面繼續分析這兩個方法的代碼實現。
4、createBeanInstance方法創建Bean的java實例對象
在createBeanInstance方法中,根據指定的初始化策略,使用靜態工廠、工廠方法或者容器的自動裝配特性生成java實例對象,創建對象的源碼如下:
//創建Bean的實例對象 protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { //檢查確認Bean是可實例化的 Class beanClass = resolveBeanClass(mbd, beanName); //使用工廠方法對Bean進行實例化 if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } if (mbd.getFactoryMethodName() != null) { //調用工廠方法實例化 return instantiateUsingFactoryMethod(beanName, mbd, args); } //使用容器的自動裝配方法進行實例化 boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { //配置了自動裝配屬性,使用容器的自動裝配實例化,容器的自動裝配是根據參數類型匹配Bean的構造方法 return autowireConstructor(beanName, mbd, null, null); } else { //使用默認的無參構造方法實例化 return instantiateBean(beanName, mbd); } } //使用Bean的構造方法進行實例化 Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { //使用容器的自動裝配特性,調用匹配的構造方法實例化 return autowireConstructor(beanName, mbd, ctors, args); } //使用默認的無參構造方法實例化 return instantiateBean(beanName, mbd); } //使用默認的無參構造方法實例化Bean對象 protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; //獲取系統的安全管理接口,JDK標准的安全管理API if (System.getSecurityManager() != null) { //這里是一個匿名內置類,根據實例化策略創建實例對象 beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return getInstantiationStrategy().instantiate(mbd, beanName, parent); } }, getAccessControlContext()); } else { //將實例化的對象封裝起來 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
經過對上面的代碼分析,我們可以看出,對使用工廠方法和自動裝配特性的Bean的實例化相當比較清楚,調用相應的工廠方法或者參數匹配的構造方法即可完成實例化對象的工作,但是對於我們最常使用的默認無參構造方法就需要使用相應的初始化策略(JDK的反射機制或者CGLIB)來進行初始化了,在方法getInstantiationStrategy().instantiate中就具體實現類使用初始策略實例化對象。
5、SimpleInstantiationStrategy類使用默認的無參構造方法創建Bean實例化對象
在使用默認的無參構造方法創建Bean的實例化對象時,方法getInstantiationStrategy().instantiate調用了SimpleInstantiationStrategy類中的實例化Bean的方法,其源碼如下:
//使用初始化策略實例化Bean對象 public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { //如果Bean定義中沒有方法覆蓋,則就不需要CGLIB父類類的方法 if (beanDefinition.getMethodOverrides().isEmpty()) { Constructor<?> constructorToUse; synchronized (beanDefinition.constructorArgumentLock) { //獲取對象的構造方法或工廠方法 constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod; //如果沒有構造方法且沒有工廠方法 if (constructorToUse == null) { //使用JDK的反射機制,判斷要實例化的Bean是否是接口 final Class clazz = beanDefinition.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { //這里是一個匿名內置類,使用反射機制獲取Bean的構造方法 constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() { public Constructor run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); } }); } else { constructorToUse = clazz.getDeclaredConstructor((Class[]) null); } beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Exception ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } //使用BeanUtils實例化,通過反射機制調用”構造方法.newInstance(arg)”來進行實例化 return BeanUtils.instantiateClass(constructorToUse); } else { //使用CGLIB來實例化對象 return instantiateWithMethodInjection(beanDefinition, beanName, owner); } }
通過上面的代碼分析,我們看到了如果Bean有方法被覆蓋了,則使用JDK的反射機制進行實例化,否則,使用CGLIB進行實例化。instantiateWithMethodInjection方法調用SimpleInstantiationStrategy的子類CglibSubclassingInstantiationStrategy使用CGLIB來進行初始化,其源碼如下:
//使用CGLIB進行Bean對象實例化 public Object instantiate(Constructor ctor, Object[] args) { //CGLIB中的類 Enhancer enhancer = new Enhancer(); //將Bean本身作為其基類 enhancer.setSuperclass(this.beanDefinition.getBeanClass()); enhancer.setCallbackFilter(new CallbackFilterImpl()); enhancer.setCallbacks(new Callback[] { NoOp.INSTANCE, new LookupOverrideMethodInterceptor(), new ReplaceOverrideMethodInterceptor() }); //使用CGLIB的create方法生成實例對象 return (ctor == null) ? enhancer.create() : enhancer.create(ctor.getParameterTypes(), args); }
CGLIB是一個常用的字節碼生成器的類庫,它提供了一系列API實現java字節碼的生成和轉換功能。我們在學習JDK的動態代理時都知道,JDK的動態代理只能針對接口,如果一個類沒有實現任何接口,要對其進行動態代理只能使用CGLIB。
6、populateBean方法對Bean屬性的依賴注入
在第3步的分析中我們已經了解到Bean的依賴注入分為以下兩個過程:
- createBeanInstance:生成Bean所包含的java對象實例。
- populateBean :對Bean屬性的依賴注入進行處理。
第4、5步中我們已經分析了容器初始化生成Bean所包含的Java實例對象的過程,現在我們繼續分析生成對象后,Spring IOC容器是如何將Bean的屬性依賴關系注入Bean實例對象中並設置好的,屬性依賴注入的代碼如下:
//將Bean屬性設置到生成的實例對象上 protected void populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) { //獲取容器在解析Bean定義資源時為BeanDefiniton中設置的屬性值 PropertyValues pvs = mbd.getPropertyValues(); //實例對象為null if (bw == null) { //屬性值不為空 if (!pvs.isEmpty()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { //實例對象為null,屬性值也為空,不需要設置屬性值,直接返回 return; } } //在設置屬性之前調用Bean的PostProcessor后置處理器 boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } if (!continueWithPropertyPopulation) { return; } //依賴注入開始,首先處理autowire自動裝配的注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); //對autowire自動裝配的處理,根據Bean名稱自動裝配注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } //根據Bean類型自動裝配注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } //檢查容器是否持有用於處理單態模式Bean關閉時的后置處理器 boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); //Bean實例對象沒有依賴,即沒有繼承基類 boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { //從實例對象中提取屬性描述符 PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; //使用BeanPostProcessor處理器處理屬性值 pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { //為要設置的屬性進行依賴檢查 checkDependencies(beanName, mbd, filteredPds, pvs); } } //對屬性進行注入 applyPropertyValues(beanName, mbd, bw, pvs); } //解析並注入依賴屬性的過程 protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { if (pvs == null || pvs.isEmpty()) { return; } //封裝屬性值 MutablePropertyValues mpvs = null; List<PropertyValue> original; if (System.getSecurityManager()!= null) { if (bw instanceof BeanWrapperImpl) { //設置安全上下文,JDK安全機制 ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext()); } } if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; //屬性值已經轉換 if (mpvs.isConverted()) { try { //為實例化對象設置屬性值 bw.setPropertyValues(mpvs); return; } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } //獲取屬性值對象的原始類型值 original = mpvs.getPropertyValueList(); } else { original = Arrays.asList(pvs.getPropertyValues()); } //獲取用戶自定義的類型轉換 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //創建一個Bean定義屬性值解析器,將Bean定義中的屬性值解析為Bean實例對象 //的實際值 BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); //為屬性的解析值創建一個拷貝,將拷貝的數據注入到實例對象中 List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size()); boolean resolveNecessary = false; for (PropertyValue pv : original) { //屬性值不需要轉換 if (pv.isConverted()) { deepCopy.add(pv); } //屬性值需要轉換 else { String propertyName = pv.getName(); //原始的屬性值,即轉換之前的屬性值 Object originalValue = pv.getValue(); //轉換屬性值,例如將引用轉換為IoC容器中實例化對象引用 Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); //轉換之后的屬性值 Object convertedValue = resolvedValue; //屬性值是否可以轉換 boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { //使用用戶自定義的類型轉換器轉換屬性值 convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } //存儲轉換后的屬性值,避免每次屬性注入時的轉換工作 if (resolvedValue == originalValue) { if (convertible) { //設置屬性轉換之后的值 pv.setConvertedValue(convertedValue); } deepCopy.add(pv); } //屬性是可轉換的,且屬性原始值是字符串類型,且屬性的原始類型值不是 //動態生成的字符串,且屬性的原始值不是集合或者數組類型 else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue) originalValue).isDynamic() && !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) { pv.setConvertedValue(convertedValue); deepCopy.add(pv); } else { resolveNecessary = true; //重新封裝屬性的值 deepCopy.add(new PropertyValue(pv, convertedValue)); } } } if (mpvs != null && !resolveNecessary) { //標記屬性值已經轉換過 mpvs.setConverted(); } //進行屬性依賴注入 try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }
分析上述代碼,我們可以看出,對屬性的注入過程分以下兩種情況:
- 屬性值類型不需要轉換時,不需要解析屬性值,直接准備進行依賴注入。
- 屬性值需要進行類型轉換時,如對其他對象的引用等,首先需要解析屬性值,然后對解析后的屬性值進行依賴注入。
對屬性值的解析是在BeanDefinitionValueResolver類中的resolveValueIfNecessary方法中進行的,對屬性值的依賴注入是通過bw.setPropertyValues方法實現的,在分析屬性值的依賴注入之前,我們先分析一下對屬性值的解析過程。
7、BeanDefinitionValueResolver解析屬性值
當容器在對屬性進行依賴注入時,如果發現屬性值需要進行類型轉換,如屬性值是容器中另一個Bean實例對象的引用,則容器首先需要根據屬性值解析出所引用的對象,然后才能將該引用對象注入到目標實例對象的屬性上去,對屬性進行解析的由resolveValueIfNecessary方法實現,其源碼如下:
//解析屬性值,對注入類型進行轉換 public Object resolveValueIfNecessary(Object argName, Object value) { //對引用類型的屬性進行解析 if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; //調用引用類型屬性的解析方法 return resolveReference(argName, ref); } //對屬性值是引用容器中另一個Bean名稱的解析 else if (value instanceof RuntimeBeanNameReference) { String refName = ((RuntimeBeanNameReference) value).getBeanName(); refName = String.valueOf(evaluate(refName)); //從容器中獲取指定名稱的Bean if (!this.beanFactory.containsBean(refName)) { throw new BeanDefinitionStoreException( "Invalid bean name '" + refName + "' in bean reference for " + argName); } return refName; } //對Bean類型屬性的解析,主要是Bean中的內部類 else if (value instanceof BeanDefinitionHolder) { BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value; return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition()); } else if (value instanceof BeanDefinition) { BeanDefinition bd = (BeanDefinition) value; return resolveInnerBean(argName, "(inner bean)", bd); } //對集合數組類型的屬性解析 else if (value instanceof ManagedArray) { ManagedArray array = (ManagedArray) value; //獲取數組的類型 Class elementType = array.resolvedElementType; if (elementType == null) { //獲取數組元素的類型 String elementTypeName = array.getElementTypeName(); if (StringUtils.hasText(elementTypeName)) { try { //使用反射機制創建指定類型的對象 elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader()); array.resolvedElementType = elementType; } catch (Throwable ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Error resolving array type for " + argName, ex); } } //沒有獲取到數組的類型,也沒有獲取到數組元素的類型,則直接設置數 //組的類型為Object else { elementType = Object.class; } } //創建指定類型的數組 return resolveManagedArray(argName, (List<?>) value, elementType); } //解析list類型的屬性值 else if (value instanceof ManagedList) { return resolveManagedList(argName, (List<?>) value); } //解析set類型的屬性值 else if (value instanceof ManagedSet) { return resolveManagedSet(argName, (Set<?>) value); } //解析map類型的屬性值 else if (value instanceof ManagedMap) { return resolveManagedMap(argName, (Map<?, ?>) value); } //解析props類型的屬性值,props其實就是key和value均為字符串的map else if (value instanceof ManagedProperties) { Properties original = (Properties) value; //創建一個拷貝,用於作為解析后的返回值 Properties copy = new Properties(); for (Map.Entry propEntry : original.entrySet()) { Object propKey = propEntry.getKey(); Object propValue = propEntry.getValue(); if (propKey instanceof TypedStringValue) { propKey = evaluate((TypedStringValue) propKey); } if (propValue instanceof TypedStringValue) { propValue = evaluate((TypedStringValue) propValue); } copy.put(propKey, propValue); } return copy; } //解析字符串類型的屬性值 else if (value instanceof TypedStringValue) { TypedStringValue typedStringValue = (TypedStringValue) value; Object valueObject = evaluate(typedStringValue); try { //獲取屬性的目標類型 Class<?> resolvedTargetType = resolveTargetType(typedStringValue); if (resolvedTargetType != null) { //對目標類型的屬性進行解析,遞歸調用 return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType); } //沒有獲取到屬性的目標對象,則按Object類型返回 else { return valueObject; } } catch (Throwable ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Error converting typed String value for " + argName, ex); } } else { return evaluate(value); } } //解析引用類型的屬性值 private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { //獲取引用的Bean名稱 String refName = ref.getBeanName(); refName = String.valueOf(evaluate(refName)); //如果引用的對象在父類容器中,則從父類容器中獲取指定的引用對象 if (ref.isToParent()) { if (this.beanFactory.getParentBeanFactory() == null) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Can't resolve reference to bean '" + refName + "' in parent factory: no parent factory available"); } return this.beanFactory.getParentBeanFactory().getBean(refName); } //從當前的容器中獲取指定的引用Bean對象,如果指定的Bean沒有被實例化,則會遞歸觸發引用Bean的初始化和依賴注入 else { Object bean = this.beanFactory.getBean(refName); //將當前實例化對象的依賴引用對象 this.beanFactory.registerDependentBean(refName, this.beanName); return bean; } } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); } } //解析array類型的屬性 private Object resolveManagedArray(Object argName, List<?> ml, Class elementType) { //創建一個指定類型的數組,用於存放和返回解析后的數組 Object resolved = Array.newInstance(elementType, ml.size()); for (int i = 0; i < ml.size(); i++) { //遞歸解析array的每一個元素,並將解析后的值設置到resolved數組中,索引為i Array.set(resolved, i, resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i))); } return resolved; } //解析list類型的屬性 private List resolveManagedList(Object argName, List<?> ml) { List<Object> resolved = new ArrayList<Object>(ml.size()); for (int i = 0; i < ml.size(); i++) { //遞歸解析list的每一個元素 resolved.add( resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i))); } return resolved; } //解析set類型的屬性 private Set resolveManagedSet(Object argName, Set<?> ms) { Set<Object> resolved = new LinkedHashSet<Object>(ms.size()); int i = 0; //遞歸解析set的每一個元素 for (Object m : ms) { resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m)); i++; } return resolved; } //解析map類型的屬性 private Map resolveManagedMap(Object argName, Map<?, ?> mm) { Map<Object, Object> resolved = new LinkedHashMap<Object, Object>(mm.size()); //遞歸解析map中每一個元素的key和value for (Map.Entry entry : mm.entrySet()) { Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey()); Object resolvedValue = resolveValueIfNecessary( new KeyedArgName(argName, entry.getKey()), entry.getValue()); resolved.put(resolvedKey, resolvedValue); } return resolved; }
通過上面的代碼分析,我們明白了Spring是如何將引用類型,內部類以及集合類型等屬性進行解析的,屬性值解析完成后就可以進行依賴注入了,依賴注入的過程就是Bean對象實例設置到它所依賴的Bean對象屬性上去,在第7步中我們已經說過,依賴注入是通過bw.setPropertyValues方法實現的,該方法也使用了委托模式,在BeanWrapper接口中至少定義了方法聲明,依賴注入的具體實現交由其實現類BeanWrapperImpl來完成,下面我們就分析依BeanWrapperImpl中賴注入相關的源碼。
8、BeanWrapperImpl對Bean屬性的依賴注入
BeanWrapperImpl類主要是對容器中完成初始化的Bean實例對象進行屬性的依賴注入,即把Bean對象設置到它所依賴的另一個Bean的屬性中去,依賴注入的相關源碼如下:
//實現屬性依賴注入功能 private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { //PropertyTokenHolder主要保存屬性的名稱、路徑,以及集合的size等信息 String propertyName = tokens.canonicalName; String actualName = tokens.actualName; //keys是用來保存集合類型屬性的size if (tokens.keys != null) { //將屬性信息拷貝 PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; getterTokens.keys = new String[tokens.keys.length - 1]; System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); Object propValue; try { //獲取屬性值,該方法內部使用JDK的內省( Introspector)機制,調用屬性//的getter(readerMethod)方法,獲取屬性的值 propValue = getPropertyValue(getterTokens); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "'", ex); } //獲取集合類型屬性的長度 String key = tokens.keys[tokens.keys.length - 1]; if (propValue == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "': returned null"); } //注入array類型的屬性值 else if (propValue.getClass().isArray()) { //獲取屬性的描述符 PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); //獲取數組的類型 Class requiredType = propValue.getClass().getComponentType(); //獲取數組的長度 int arrayIndex = Integer.parseInt(key); Object oldValue = null; try { //獲取數組以前初始化的值 if (isExtractOldValueForEditor()) { oldValue = Array.get(propValue, arrayIndex); } //將屬性的值賦值給數組中的元素 Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType)); Array.set(propValue, arrayIndex, convertedValue); } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Invalid array index in property path '" + propertyName + "'", ex); } } //注入list類型的屬性值 else if (propValue instanceof List) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); //獲取list集合的類型 Class requiredType = GenericCollectionTypeResolver.getCollectionReturnType( pd.getReadMethod(), tokens.keys.length); List list = (List) propValue; //獲取list集合的size int index = Integer.parseInt(key); Object oldValue = null; if (isExtractOldValueForEditor() && index < list.size()) { oldValue = list.get(index); } //獲取list解析后的屬性值 Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType)); if (index < list.size()) { //為list屬性賦值 list.set(index, convertedValue); } //如果list的長度大於屬性值的長度,則多余的元素賦值為null else if (index >= list.size()) { for (int i = list.size(); i < index; i++) { try { list.add(null); } catch (NullPointerException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot set element with index " + index + " in List of size " + list.size() + ", accessed using property path '" + propertyName + "': List does not support filling up gaps with null elements"); } } list.add(convertedValue); } } //注入map類型的屬性值 else if (propValue instanceof Map) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); //獲取map集合key的類型 Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType( pd.getReadMethod(), tokens.keys.length); //獲取map集合value的類型 Class mapValueType = GenericCollectionTypeResolver.getMapValueReturnType( pd.getReadMethod(), tokens.keys.length); Map map = (Map) propValue; //解析map類型屬性key值 Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), mapKeyType)); Object oldValue = null; if (isExtractOldValueForEditor()) { oldValue = map.get(convertedMapKey); } //解析map類型屬性value值 Object convertedMapValue = convertIfNecessary( propertyName, oldValue, pv.getValue(), mapValueType, new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1))); //將解析后的key和value值賦值給map集合屬性 map.put(convertedMapKey, convertedMapValue); } else { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + "' is neither an array nor a List nor a Map; returned value was [" + pv.getValue() + "]"); } } //對非集合類型的屬性注入 else { PropertyDescriptor pd = pv.resolvedDescriptor; if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) { pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); //無法獲取到屬性名或者屬性沒有提供setter(寫方法)方法 if (pd == null || pd.getWriteMethod() == null) { //如果屬性值是可選的,即不是必須的,則忽略該屬性值 if (pv.isOptional()) { logger.debug("Ignoring optional value for property '" + actualName + "' - property not found on bean class [" + getRootClass().getName() + "]"); return; } //如果屬性值是必須的,則拋出無法給屬性賦值,因為每天提供setter方法異常 else { PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass()); throw new NotWritablePropertyException( getRootClass(), this.nestedPath + propertyName, matches.buildErrorMessage(), matches.getPossibleMatches()); } } pv.getOriginalPropertyValue().resolvedDescriptor = pd; } Object oldValue = null; try { Object originalValue = pv.getValue(); Object valueToApply = originalValue; if (!Boolean.FALSE.equals(pv.conversionNecessary)) { if (pv.isConverted()) { valueToApply = pv.getConvertedValue(); } else { if (isExtractOldValueForEditor() && pd.getReadMethod() != null) { //獲取屬性的getter方法(讀方法),JDK內省機制 final Method readMethod = pd.getReadMethod(); //如果屬性的getter方法不是public訪問控制權限的,即訪問控制權限比較嚴格, //則使用JDK的反射機制強行訪問非public的方法(暴力讀取屬性值) if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) { if (System.getSecurityManager()!= null) { //匿名內部類,根據權限修改屬性的讀取控制限制 AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { readMethod.setAccessible(true); return null; } }); } else { readMethod.setAccessible(true); } } try { //屬性沒有提供getter方法時,調用潛在的讀取屬性值//的方法,獲取屬性值 if (System.getSecurityManager() != null) { oldValue = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { return readMethod.invoke(object); } }, acc); } else { oldValue = readMethod.invoke(object); } } catch (Exception ex) { if (ex instanceof PrivilegedActionException) { ex = ((PrivilegedActionException) ex).getException(); } if (logger.isDebugEnabled()) { logger.debug("Could not read previous value of property '" + this.nestedPath + propertyName + "'", ex); } } } //設置屬性的注入值 valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd); } pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } //根據JDK的內省機制,獲取屬性的setter(寫方法)方法 final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() : pd.getWriteMethod()); //如果屬性的setter方法是非public,即訪問控制權限比較嚴格,則使用JDK的反射機制, //強行設置setter方法可訪問(暴力為屬性賦值) if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) { //如果使用了JDK的安全機制,則需要權限驗證 if (System.getSecurityManager()!= null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { writeMethod.setAccessible(true); return null; } }); } else { writeMethod.setAccessible(true); } } final Object value = valueToApply; if (System.getSecurityManager() != null) { try { //將屬性值設置到屬性上去 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { writeMethod.invoke(object, value); return null; } }, acc); } catch (PrivilegedActionException ex) { throw ex.getException(); } } else { writeMethod.invoke(this.object, value); } } catch (TypeMismatchException ex) { throw ex; } catch (InvocationTargetException ex) { PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue()); if (ex.getTargetException() instanceof ClassCastException) { throw new TypeMismatchException(propertyChangeEvent, pd.getPropertyType(), ex.getTargetException()); } else { throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException()); } } catch (Exception ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue()); throw new MethodInvocationException(pce, ex); } } }
通過對上面注入依賴代碼的分析,我們已經明白了Spring IOC容器是如何將屬性的值注入到Bean實例對象中去的:
- 對於集合類型的屬性,將其屬性值解析為目標類型的集合后直接賦值給屬性。
- 對於非集合類型的屬性,大量使用了JDK的反射和內省機制,通過屬性的getter方法(reader method)獲取指定屬性注入以前的值,同時調用屬性的setter方法(writer method)為屬性設置注入后的值。看到這里相信很多人都明白了Spring的setter注入原理。
至此Spring IOC容器對Bean定義資源文件的定位,載入、解析和依賴注入已經全部分析完畢,現在Spring IOC容器中管理了一系列靠依賴關系聯系起來的Bean,程序不需要應用自己手動創建所需的對象,Spring IOC容器會在我們使用的時候自動為我們創建,並且為我們注入好相關的依賴,這就是Spring核心功能的控制反轉和依賴注入的相關功能。
六、IOC容器高級特性
1、介紹
通過前面對Spring IoC容器的源碼分析,我們已經基本上了解了Spring IOC容器對Bean定義資源的定位、讀入和解析過程,同時也清楚了當用戶通過getBean方法向IOC容器獲取被管理的Bean時,IOC容器對Bean進行的初始化和依賴注入過程,這些是Spring IOC容器的基本功能特性。Spring IOC容器還有一些高級特性,如使用lazy-init屬性對Bean預初始化、FactoryBean產生或者修飾Bean對象的生成、IOC容器初始化Bean過程中使用BeanPostProcessor后置處理器對Bean聲明周期事件管理和IOC容器的autowiring自動裝配功能等。
2、Spring IoC容器的lazy-init屬性實現預實例化
通過前面我們對IOC容器的實現和工作原理分析,我們知道IOC容器的初始化過程就是對Bean定義資源的定位、載入和注冊,此時容器對Bean的依賴注入並沒有發生,依賴注入主要是在應用程序第一次向容器索取Bean時,通過getBean方法的調用完成。
當Bean定義資源的<Bean>元素中配置了lazy-init屬性時,容器將會在初始化的時候對所配置的Bean進行預實例化,Bean的依賴注入在容器初始化的時候就已經完成。這樣,當應用程序第一次向容器索取被管理的Bean時,就不用再初始化和對Bean進行依賴注入了,直接從容器中獲取已經完成依賴注入的現成Bean,可以提高應用第一次向容器獲取Bean的性能。
下面我們通過代碼分析容器預實例化的實現過程:
(1). refresh()
先從IOC容器的初始化過程開始,通過前面文章分析,我們知道IOC容器讀入已經定位的Bean定義資源是從refresh方法開始的,我們首先從AbstractApplicationContext類的refresh方法入手分析,源碼如下:
//容器初始化的過程,讀入Bean定義資源,並解析注冊 public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //調用容器准備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識 prepareRefresh(); //告訴子類啟動refreshBeanFactory()方法,Bean定義資源文件的載入從 //子類的refreshBeanFactory()方法啟動 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //為BeanFactory配置容器特性,例如類加載器、事件處理器等 prepareBeanFactory(beanFactory); try { //為容器的某些子類指定特殊的BeanPost事件處理器 postProcessBeanFactory(beanFactory); //調用所有注冊的BeanFactoryPostProcessor的Bean invokeBeanFactoryPostProcessors(beanFactory); //為BeanFactory注冊BeanPost事件處理器. //BeanPostProcessor是Bean后置處理器,用於監聽容器觸發的事件 registerBeanPostProcessors(beanFactory); //初始化信息源,和國際化相關. initMessageSource(); //初始化容器事件傳播器. initApplicationEventMulticaster(); //調用子類的某些特殊Bean初始化方法 onRefresh(); //為事件傳播器注冊事件監聽器. registerListeners(); //這里是對容器lazy-init屬性進行處理的入口方法 finishBeanFactoryInitialization(beanFactory); //初始化容器的生命周期事件處理器,並發布容器的生命周期事件 finishRefresh(); } catch (BeansException ex) { //銷毀以創建的單態Bean destroyBeans(); //取消refresh操作,重置容器的同步標識. cancelRefresh(ex); throw ex; } } }
在refresh方法中ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();啟動了Bean定義資源的載入、注冊過程,而finishBeanFactoryInitialization方法是對注冊后的Bean定義中的預實例化(lazy-init=false,Spring默認就是預實例化,即為true)的Bean進行處理的地方。
(2). finishBeanFactoryInitialization處理預實例化Bean
當Bean定義資源被載入IOC容器之后,容器將Bean定義資源解析為容器內部的數據結構BeanDefinition注冊到容器中,AbstractApplicationContext類中的finishBeanFactoryInitialization方法對配置了預實例化屬性的Bean進行預初始化過程,源碼如下:
//對配置了lazy-init屬性的Bean進行預實例化處理 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { //這是Spring3以后新加的代碼,為容器指定一個轉換服務(ConversionService) //在對某些Bean屬性進行轉換時使用 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } //為了類型匹配,停止使用臨時的類加載器 beanFactory.setTempClassLoader(null); //緩存容器中所有注冊的BeanDefinition元數據,以防被修改 beanFactory.freezeConfiguration(); //對配置了lazy-init屬性的單態模式Bean進行預實例化處理 beanFactory.preInstantiateSingletons(); }
ConfigurableListableBeanFactory是一個接口,其preInstantiateSingletons方法由其子類DefaultListableBeanFactory提供。
(3)、DefaultListableBeanFactory對配置lazy-init屬性單態Bean的預實例化
//對配置lazy-init屬性單態Bean的預實例化 public void preInstantiateSingletons() throws BeansException { if (this.logger.isInfoEnabled()) { this.logger.info("Pre-instantiating singletons in " + this); } //在對配置lazy-init屬性單態Bean的預實例化過程中,必須多線程同步,以確保數據一致性 synchronized (this.beanDefinitionMap) { for (String beanName : this.beanDefinitionNames) { //獲取指定名稱的Bean定義 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); //Bean不是抽象的,是單態模式的,且lazy-init屬性配置為false if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果指定名稱的bean是創建容器的Bean if (isFactoryBean(beanName)) { //FACTORY_BEAN_PREFIX=”&”,當Bean名稱前面加”&”符號 //時,獲取的是產生容器對象本身,而不是容器產生的Bean. //調用getBean方法,觸發容器對Bean實例化和依賴注入過程 final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); //標識是否需要預實例化 boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { //一個匿名內部類 isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { return ((SmartFactoryBean) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit(); } if (isEagerInit) { //調用getBean方法,觸發容器對Bean實例化和依賴注入過程 getBean(beanName); } } else { //調用getBean方法,觸發容器對Bean實例化和依賴注入過程 getBean(beanName); } } } } }
通過對lazy-init處理源碼的分析,我們可以看出,如果設置了lazy-init屬性,則容器在完成Bean定義的注冊之后,會通過getBean方法,觸發對指定Bean的初始化和依賴注入過程,這樣當應用第一次向容器索取所需的Bean時,容器不再需要對Bean進行初始化和依賴注入,直接從已經完成實例化和依賴注入的Bean中取一個線程的Bean,這樣就提高了第一次獲取Bean的性能。
3、FactoryBean的實現
在Spring中,有兩個很容易混淆的類:BeanFactory和FactoryBean。
- BeanFactory:Bean工廠,是一個工廠(Factory),我們Spring IoC容器的最頂層接口就是這個BeanFactory,它的作用是管理Bean,即實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。
- FactoryBean:工廠Bean,是一個Bean,作用是產生其他bean實例。通常情況下,這種bean沒有什么特別的要求,僅需要提供一個工廠方法,該方法用來返回其他bean實例。通常情況下,bean無須自己實現工廠模式,Spring容器擔任工廠角色;但少數情況下,容器中的bean本身就是工廠,其作用是產生其它bean實例。
當用戶使用容器本身時,可以使用轉義字符”&”來得到FactoryBean本身,以區別通過FactoryBean產生的實例對象和FactoryBean對象本身。在BeanFactory中通過如下代碼定義了該轉義字符:
String FACTORY_BEAN_PREFIX = "&";
如果myJndiObject是一個FactoryBean,則使用&myJndiObject得到的是myJndiObject對象,而不是myJndiObject產生出來的對象。
(1). FactoryBean的源碼
//工廠Bean,用於產生其他對象 public interface FactoryBean<T> { //獲取容器管理的對象實例 T getObject() throws Exception; //獲取Bean工廠創建的對象的類型 Class<?> getObjectType(); //Bean工廠創建的對象是否是單態模式,如果是單態模式,則整個容器中只有一個實例 //對象,每次請求都返回同一個實例對象 boolean isSingleton(); }
(2). AbstractBeanFactory的getBean方法調用FactoryBean
在前面我們分析Spring IOC 容器實例化Bean並進行依賴注入過程的源碼時,提到在getBean方法觸發容器實例化Bean的時候會調用AbstractBeanFactory的doGetBean方法來進行實例化的過程,源碼如下:
//真正實現向IoC容器獲取Bean的功能,也是觸發依賴注入功能的地方 @SuppressWarnings("unchecked") protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { //根據指定的名稱獲取被管理Bean的名稱,剝離指定名稱中對容器的相關依賴,如果指定的是別名,將別名轉換為規范的Bean名稱 final String beanName = transformedBeanName(name); Object bean; //先從緩存中取是否已經有被創建過的單態類型的Bean,對於單態模式的Bean整個IoC容器中只創建一次,不需要重復創建 Object sharedInstance = getSingleton(beanName); //IoC容器創建單態模式Bean實例對象 if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { //如果指定名稱的Bean在容器中已有單態模式的Bean被創建,直接返回已經創建的Bean if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } //獲取給定Bean的實例對象,主要是完成FactoryBean的相關處理 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } …… } //獲取給定Bean的實例對象,主要是完成FactoryBean的相關處理 protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { //容器已經得到了Bean實例對象,這個實例對象可能是一個普通的Bean,也可能是一個工廠Bean,如果是一個工廠Bean,則使用它創建一個Bean實例對象 //如果調用本身就想獲得一個容器的引用,則指定返回這個工廠Bean實例對象 //如果指定的名稱是容器的解引用(dereference,即是對象本身而非內存地址),且Bean實例也不是創建Bean實例對象的工廠Bean if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } //如果Bean實例不是工廠Bean,或者指定名稱是容器的解引用,調用者向獲取對容器的引用,則直接返回當前的Bean實例 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } //處理指定名稱不是容器的解引用,或者根據名稱獲取的Bean實例對象是一個工廠Bean,使用工廠Bean創建一個Bean的實例對象 Object object = null; if (mbd == null) { //從Bean工廠緩存中獲取給定名稱的Bean實例對象 object = getCachedObjectForFactoryBean(beanName); } //讓Bean工廠生產給定名稱的Bean對象實例 if (object == null) { FactoryBean factory = (FactoryBean) beanInstance; //如果從Bean工廠生產的Bean是單態模式的,則緩存 if (mbd == null && containsBeanDefinition(beanName)) { //從容器中獲取指定名稱的Bean定義,如果繼承基類,則合並基類相關屬性 mbd = getMergedLocalBeanDefinition(beanName); } //如果從容器得到Bean定義信息,並且Bean定義信息不是虛構的,則讓工廠Bean生產Bean實例對象 boolean synthetic = (mbd != null && mbd.isSynthetic()); //調用FactoryBeanRegistrySupport類的getObjectFromFactoryBean方法,實現工廠Bean生產Bean對象實例的過程 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
在上面獲取給定Bean的實例對象的getObjectForBeanInstance方法中,會調用FactoryBeanRegistrySupport類的getObjectFromFactoryBean方法,該方法實現了Bean工廠生產Bean實例對象。
Dereference(解引用):一個在C/C++中應用比較多的術語,在C++中,”*”是解引用符號,而”&”是引用符號,解引用是指變量指向的是所引用對象的本身數據,而不是引用對象的內存地址。
(3)、AbstractBeanFactory生產Bean實例對象
AbstractBeanFactory類中生產Bean實例對象的主要源碼如下:
//Bean工廠生產Bean實例對象 protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) { //Bean工廠是單態模式,並且Bean工廠緩存中存在指定名稱的Bean實例對象 if (factory.isSingleton() && containsSingleton(beanName)) { //多線程同步,以防止數據不一致 synchronized (getSingletonMutex()) { //直接從Bean工廠緩存中獲取指定名稱的Bean實例對象 Object object = this.factoryBeanObjectCache.get(beanName); //Bean工廠緩存中沒有指定名稱的實例對象,則生產該實例對象 if (object == null) { //調用Bean工廠的getObject方法生產指定Bean的實例對象 object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess); //將生產的實例對象添加到Bean工廠緩存中 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT)); } return (object != NULL_OBJECT ? object : null); } } //調用Bean工廠的getObject方法生產指定Bean的實例對象 else { return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess); } } //調用Bean工廠的getObject方法生產指定Bean的實例對象 private Object doGetObjectFromFactoryBean( final FactoryBean factory, final String beanName, final boolean shouldPostProcess) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { //實現PrivilegedExceptionAction接口的匿名內置類,根據JVM檢查權限,然后決定BeanFactory創建實例對象 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { //調用BeanFactory接口實現類的創建對象方法 return factory.getObject(); } }, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //調用BeanFactory接口實現類的創建對象方法 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); } //創建出來的實例對象為null,或者因為單態對象正在創建而返回null if (object == null && isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } //為創建出來的Bean實例對象添加BeanPostProcessor后置處理器 if (object != null && shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex); } } return object; }
從上面的源碼分析中,我們可以看出,BeanFactory接口調用其實現類的getObject方法來實現創建Bean實例對象的功能。
(4). 工廠Bean的實現類getObject方法創建Bean實例對象
FactoryBean的實現類有非常多,比如:Proxy、RMI、JNDI、ServletContextFactoryBean等等,FactoryBean接口為Spring容器提供了一個很好的封裝機制,具體的getObject有不同的實現類根據不同的實現策略來具體提供,我們分析一個最簡單的AnnotationTestFactoryBean的實現源碼:
public class AnnotationTestBeanFactory implements FactoryBean<IJmxTestBean> { private final FactoryCreatedAnnotationTestBean instance = new FactoryCreatedAnnotationTestBean(); public AnnotationTestBeanFactory() { this.instance.setName("FACTORY"); } //AnnotationTestBeanFactory產生Bean實例對象的實現 public IJmxTestBean getObject() throws Exception { return this.instance; } public Class<? extends IJmxTestBean> getObjectType() { return FactoryCreatedAnnotationTestBean.class; } public boolean isSingleton() { return true; } }
其他的Proxy,RMI,JNDI等等,都是根據相應的策略提供getObject的實現。這里不做一一分析,這已經不是Spring的核心功能,有需要的時候再去深入研究。
4. BeanPostProcessor后置處理器的實現
BeanPostProcessor后置處理器是Spring IoC容器經常使用到的一個特性,這個Bean后置處理器是一個監聽器,可以監聽容器觸發的Bean聲明周期事件。后置處理器向容器注冊以后,容器中管理的Bean就具備了接收IOC容器事件回調的能力。
BeanPostProcessor的使用非常簡單,只需要提供一個實現接口BeanPostProcessor的實現類,然后在Bean的配置文件中設置即可。
(1). BeanPostProcessor源碼
package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; public interface BeanPostProcessor { //為在Bean的初始化前提供回調入口 Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; //為在Bean的初始化之后提供回調入口 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
這兩個回調的入口都是和容器管理的Bean的生命周期事件緊密相關,可以為用戶提供在Spring IOC容器初始化Bean過程中自定義的處理操作。
(2). AbstractAutowireCapableBeanFactory類對容器生成的Bean添加后置處理器
BeanPostProcessor后置處理器的調用發生在Spring IOC容器完成對Bean實例對象的創建和屬性的依賴注入完成之后,在對Spring依賴注入的源碼分析過程中我們知道,當應用程序第一次調用getBean方法(lazy-init預實例化除外)向Spring IoC容器索取指定Bean時觸發Spring IoC容器創建Bean實例對象並進行依賴注入的過程,其中真正實現創建Bean對象並進行依賴注入的方法是AbstractAutowireCapableBeanFactory類的doCreateBean方法,主要源碼如下:
//真正創建Bean的方法 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { //創建Bean實例對象 …… try { //對Bean屬性進行依賴注入 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { //在對Bean實例對象生成和依賴注入完成以后,開始對Bean實例對象/進行初始化 ,為Bean實例對象應用BeanPostProcessor后置處理器 exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } …… //為應用返回所需要的實例對象 return exposedObject; }
從上面的代碼中我們知道,為Bean實例對象添加BeanPostProcessor后置處理器的入口的是initializeBean方法。
(3). initializeBean方法為容器產生的Bean實例對象添加BeanPostProcessor后置處理器
同樣在AbstractAutowireCapableBeanFactory類中,initializeBean方法實現為容器創建的Bean實例對象添加BeanPostProcessor后置處理器,源碼如下:
//初始容器創建的Bean實例對象,為其添加BeanPostProcessor后置處理器 protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { //JDK的安全機制驗證權限 if (System.getSecurityManager() != null) { //實現PrivilegedAction接口的匿名內部類 AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { //為Bean實例對象包裝相關屬性,如名稱,類加載器,所屬容器等信息 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; //對BeanPostProcessor后置處理器的postProcessBeforeInitialization回調方法的調用,為Bean實例初始化前做一些處理 if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } //調用Bean實例對象初始化的方法,這個初始化方法是在Spring Bean定義配置文件中通過init-method屬性指定的 try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } //對BeanPostProcessor后置處理器的postProcessAfterInitialization回調方法的調用,為Bean實例初始化之后做一些處理 if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } //調用BeanPostProcessor后置處理器實例對象初始化之前的處理方法 public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //遍歷容器為所創建的Bean添加的所有BeanPostProcessor后置處理器 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { //調用Bean實例所有的后置處理中的初始化前處理方法,為Bean實例對象在初始化之前做一些自定義的處理操作 result = beanProcessor.postProcessBeforeInitialization(result, beanName); if (result == null) { return result; } } return result; } //調用BeanPostProcessor后置處理器實例對象初始化之后的處理方法 public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //遍歷容器為所創建的Bean添加的所有BeanPostProcessor后置處理器 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { //調用Bean實例所有的后置處理中的初始化后處理方法,為Bean實例對象在初始化之后做一些自定義的處理操作 result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
BeanPostProcessor是一個接口,其初始化前的操作方法和初始化后的操作方法均委托其實現子類來實現,在Spring中,BeanPostProcessor的實現子類非常的多,分別完成不同的操作,如:AOP面向切面編程的注冊通知適配器、Bean對象的數據校驗、Bean繼承屬性/方法的合並等等,我們以最簡單的AOP切面織入來簡單了解其主要的功能。
(4). AdvisorAdapterRegistrationManager在Bean對象初始化后注冊通知適配器
AdvisorAdapterRegistrationManager是BeanPostProcessor的一個實現類,其主要的作用為容器中管理的Bean注冊一個面向切面編程的通知適配器,以便在Spring容器為所管理的Bean進行面向切面編程時提供方便,其源碼如下:
//為容器中管理的Bean注冊一個面向切面編程的通知適配器 public class AdvisorAdapterRegistrationManager implements BeanPostProcessor { //容器中負責管理切面通知適配器注冊的對象 private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) { this.advisorAdapterRegistry = advisorAdapterRegistry; } //BeanPostProcessor在Bean對象初始化前的操作 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //沒有做任何操作,直接返回容器創建的Bean對象 return bean; } //BeanPostProcessor在Bean對象初始化后的操作 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof AdvisorAdapter){ //如果容器創建的Bean實例對象是一個切面通知適配器,則向容器的注冊 this.advisorAdapterRegistry.registerAdvisorAdapter((AdvisorAdapter) bean); } return bean; } }
其他的BeanPostProcessor接口實現類的也類似,都是對Bean對象使用到的一些特性進行處理,或者向IOC容器中注冊,為創建的Bean實例對象做一些自定義的功能增加,這些操作是容器初始化Bean時自動觸發的,不需要人為的干預。
5. Spring IOC容器autowiring實現原理
Spring IOC容器提供了兩種管理Bean依賴關系的方式:
- 顯式管理:通過BeanDefinition的屬性值和構造方法實現Bean依賴關系管理。
- autowiring:Spring IoC容器的依賴自動裝配功能,不需要對Bean屬性的依賴關系做顯式的聲明,只需要在配置好autowiring屬性,IOC容器會自動使用反射查找屬性的類型和名稱,然后基於屬性的類型或者名稱來自動匹配容器中管理的Bean,從而自動地完成依賴注入。
通過對autowiring自動裝配特性的理解,我們知道容器對Bean的自動裝配發生在容器對Bean依賴注入的過程中。在前面對Spring IOC容器的依賴注入過程源碼分析中,我們已經知道了容器對Bean實例對象的屬性注入的處理發生在AbstractAutoWireCapableBeanFactory類中的populateBean方法中,我們通過程序流程分析autowiring的實現原理。
(1). AbstractAutoWireCapableBeanFactory對Bean實例進行屬性依賴注入
應用第一次通過getBean方法(配置了lazy-init預實例化屬性的除外)向IOC容器索取Bean時,容器創建Bean實例對象,並且對Bean實例對象進行屬性依賴注入,AbstractAutoWireCapableBeanFactory的性依賴注入的功能,其主要源碼如下:
protected void populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) { //獲取Bean定義的屬性值,並對屬性值進行處理 PropertyValues pvs = mbd.getPropertyValues(); …… //對依賴注入處理,首先處理autowiring自動裝配的依賴注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); //根據Bean名稱進行autowiring自動裝配處理 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } //根據Bean類型進行autowiring自動裝配處理 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } } //對非autowiring的屬性進行依賴注入處理 …… }
(2). Spring IOC容器根據Bean名稱或者類型進行autowiring自動依賴注入
//根據名稱對屬性進行自動依賴注入 protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //對Bean對象中非簡單屬性(不是簡單繼承的對象,如8中原始類型,字符串,URL等//都是簡單屬性)進行處理 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { //如果Spring IOC容器中包含指定名稱的Bean if (containsBean(propertyName)) { //調用getBean方法向IoC容器索取指定名稱的Bean實例,迭代觸發屬性的//初始化和依賴注入 Object bean = getBean(propertyName); //為指定名稱的屬性賦予屬性值 pvs.add(propertyName, bean); //指定名稱屬性注冊依賴Bean名稱,進行屬性依賴注入 registerDependentBean(propertyName, beanName); if (logger.isDebugEnabled()) { logger.debug("Added autowiring by name from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + propertyName + "'"); } } else { if (logger.isTraceEnabled()) { logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName + "' by name: no matching bean found"); } } } } //根據類型對屬性進行自動依賴注入 protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //獲取用戶定義的類型轉換器 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //存放解析的要注入的屬性 Set<String> autowiredBeanNames = new LinkedHashSet<String>(4); //對Bean對象中非簡單屬性(不是簡單繼承的對象,如8中原始類型、字符、URL等都是簡單屬性)進行處理 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { //獲取指定屬性名稱的屬性描述器 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); //不對Object類型的屬性進行autowiring自動依賴注入 if (!Object.class.equals(pd.getPropertyType())) { //獲取屬性的setter方法 MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); //檢查指定類型是否可以被轉換為目標對象的類型 boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass()); //創建一個要被注入的依賴描述 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); //根據容器的Bean定義解析依賴關系,返回所有要被注入的Bean對象 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { //為屬性賦值所引用的對象 pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { //指定名稱屬性注冊依賴Bean名稱,進行屬性依賴注入 registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + autowiredBeanName + "'"); } } //釋放已自動注入的屬性 autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } } }
通過上面的源碼分析,我們可以看出來通過屬性名進行自動依賴注入的相對比通過屬性類型進行自動依賴注入要稍微簡單一些,但是真正實現屬性注入的是DefaultSingletonBeanRegistry類的registerDependentBean方法。
(3). DefaultSingletonBeanRegistry的registerDependentBean方法對屬性注入
//為指定的Bean注入依賴的Bean public void registerDependentBean(String beanName, String dependentBeanName) { //處理Bean名稱,將別名轉換為規范的Bean名稱 String canonicalName = canonicalName(beanName); //多線程同步,保證容器內數據的一致性 。先從容器中:bean名稱-->全部依賴Bean名稱集合找查找給定名稱Bean的依賴Bean synchronized (this.dependentBeanMap) { //獲取給定名稱Bean的所有依賴Bean名稱 Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { //為Bean設置依賴Bean信息 dependentBeans = new LinkedHashSet<String>(8); this.dependentBeanMap.put(canonicalName, dependentBeans); } //向容器中:bean名稱-->全部依賴Bean名稱集合添加Bean的依賴信息,即:將Bean所依賴的Bean添加到容器的集合中 dependentBeans.add(dependentBeanName); } //從容器中:bean名稱-->指定名稱Bean的依賴Bean集合找查找給定名稱Bean的依賴Bean synchronized (this.dependenciesForBeanMap) { Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName); if (dependenciesForBean == null) { dependenciesForBean = new LinkedHashSet<String>(8); this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean); } //向容器中:bean名稱-->指定Bean的依賴Bean名稱集合添加Bean的依賴信息。即:將Bean所依賴的Bean添加到容器的集合中 dependenciesForBean.add(canonicalName); } }
通過對autowiring的源碼分析,我們可以看出,autowiring的實現過程:
- a. 對Bean的屬性迭代調用getBean方法,完成依賴Bean的初始化和依賴注入。
- b. 將依賴Bean的屬性引用設置到被依賴的Bean屬性上。
- c. 將依賴Bean的名稱和被依賴Bean的名稱存儲在IOC容器的集合中。
Spring IoC容器的autowiring屬性自動依賴注入是一個很方便的特性,可以簡化開發時的配置,但是凡是都有兩面性,自動屬性依賴注入也有不足,首先,Bean的依賴關系在配置文件中無法很清楚地看出來,對於維護造成一定困難。其次,由於自動依賴注入是Spring容器自動執行的,容器是不會智能判斷的,如果配置不當,將會帶來無法預料的后果,所以自動依賴注入特性在使用時還是綜合考慮。
6、多容器/父子容器概念
Spring 框架允許在一個應用中創建多個上下文容器。但是建議容器之間有父子關系。可以通過 ConfigurableApplicationContext 接口中定義的 setParent 方法設置父容器。一旦設置父 子關系,則可以通過子容器獲取父容器中除 PropertyPlaceHolder 以外的所有資源,父容器不能獲取子容器中的任意資源(類似 Java 中的類型繼承)。
典型的父子容器: spring 和 springmvc 同時使用的時候。ContextLoaderListener 創建的容器是父容器,DispatcherServlet 創建的容器是子容器。
父子容器的存在是保證一個 JVM 中,只有一個樹狀結構的容器樹,每個容器都有一定的作用范圍和訪問域,起到一個隔離的作用。可以通過子容器訪問父容器資源。
7、p 域/c 域
Spring2.0 之后引入了 p(property 標簽)域、Spring3.1 之后引入了 c(constractor-arg 標簽) 域。可以簡化配置文件中對 property 和 constructor-arg 的配置。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.spring framework.org/schema /c" xmlns:p="http://www.sprin gframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org /schema/beans http://www.springframework.org /schema /beans/spri ng-bean s.xsd"> <bean id="oneBean" class="com.sxt.OneBean" p:a="10" p:o-ref="otherBean" c:a="20" c:o-ref="otherBean"/> <bean id="otherBean" class="com.sxt.OtherBean" /> </beans>
class OneBean{ int a; Object o; public OneBean(int a, Object o){ this.a = a; this.o = o; } // getters and setters for fields. }
8、lookup-method
lookup-method 一旦應用,Spring 框架會自動使用 CGLIB 技術為指定類型創建一個動態子類型,並自動實現抽象方法。可以動態的實現依賴注入的數據准備。
在效率上,比直接自定義子類型慢。相對來說更加通用。可以只提供 lookup-method 方法的返回值對象即可實現動態的對象返回。
在工廠方法難以定制的時候使用,也是模板的一種應用,工廠方法的擴展。如:工廠方法返回對象類型為接口類型,且不同版本應用返回的對象未必相同時使用,可以避免多次開發工廠類。如:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestLookupMethod { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:lookupmethod/applicationContext.xml"); CommandManager manager = context.getBean("manager", CommandManager.class); System.out.println(manager.getClass().getName()); manager.process(); } } abstract class CommandManager { public void process() { MyCommand command = createCommand(); // do something ... System.out.println(command); } protected abstract MyCommand createCommand(); } interface MyCommand { } class MyCommand1 implements MyCommand { public MyCommand1() { System.out.println("MyCommand1 instanced"); } } class MyCommand2 implements MyCommand { public MyCommand2() { System.out.println("MyCommand2 instanced"); } }
applicationContext.xml中配置為:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="manager" class="com.sxt.lookupmethod.CommandManager"> <lookup-method bean="command2" name="createCommand"/> </bean> <bean id="command1" class="com.sxt.lookupmethod.MyCommand1"></bean> <bean id="command2" class="com.sxt.lookupmethod.MyCommand2"></bean> </beans>