目錄java
在Spring框架中最重要的是Spring IoC容器,它是Spring框架的核心。本文將從更高的角度來解析Sping IoC容器,瞭解其是如何設計的。瞭解同樣東西最好的辦法是從其核心本質出發,只要把握住了這樣一個核心,其餘的一些東西也迎刃而解了。這是一個很好的開端,咱們一塊兒開始吧...程序員
org.springframework.context.ApplicationContext
接口表明Spring IoC容器,主要負責bean的實例化、配置、裝配,簡而言之,Spring IoC容器是管理這些bean的(這裏所說的bean指的是組成你的應用程序中的對象,而且這些對象被Spring所管理)。容器如何知道哪些對象要進行實例化、配置和裝配的呢?是經過讀取配置文件元數據來達到這個效果的,配置文件元數據是用xml配置、Java註解和Java代碼配置來表示的。這使得做爲程序員的咱們,只須要向Spring容器提供配置元數據,Spring容器就能在咱們的應用中實例化、配置和裝配這些對象。org.springframework.beans
和org.springframework.context
包是Spring IoC容器的基礎。Spring提供了不少Application
接口的實現。在單獨的應用中,建立ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
的實例是很是經常使用的作法。示例以下:web
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Hello hello = (Hello) ac.getBean("hello"); hello.sayHello();
然而在大部分的應用場景中,不須要實例化一個或者多個Spring IoC容器的實例。例如在web應用的場景下,只須要在web.xml中建立七行樣板配置的代碼以下:spring
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</paramvalue> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
下面這張圖從更高的視角展現了Spring是怎樣工做的。你的應用程序中的類是和配置元數據組合在一塊兒,以便在ApplicationContext
建立和初始化以後,你擁有了一個徹底配置的、可執行的系統。編程
爲了方便對ApplicationContext
接口的層次結構有一個大概的認識,下面使用IDEA來生成ApplicationContext
的繼承關係圖。(選中ApplicationContext接口->右鍵->Diagrams->Show Diagrams...)app
(舒適提示:點擊圖片能夠查看高清大圖)框架
從上圖就能很清楚的看出ApplicationContext
繼承的接口分爲五類:ide
BeanFactory
:提供了可以管理任何對象的高級配置機制,這個接口是Spring框架中比較重要的一個接口。
ListableBeanFactory
:從該接口的名字就能知道,該接口除了擁有BeanFactory的功能外,該接口還有能列出factory中全部bean的實例的能力。HierarchicalBeanFactory
:該接口除了擁有BeanFactory的功能外,還提供了BeanFactory分層的機制,查找bean的時候,除了在自身BeanFactory查找外,若是沒有查找到,還會在父級BeanFactory進行查找。MessageSource
:消息資源的處理,用於國際化。ApplicationEventPublisher
:用於處理事件發佈機制。EnvironmentCapable
:提供了Environment
的訪問能力。ResourceLoader
:用於加載資源的策略接口(例如類路徑下的資源、系統文件下的資源等等)。
ResourcePatternResolver
:用於將位置模式(例如Ant風格的路徑模式)解析成資源對象的策略接口。classpath*:
前綴能匹配因此類路徑下的資源。先看一下在ApplicationContext
中定義的方法:函數式編程
String getId(); // 獲取ApplicationContext的惟一id String getApplicationName(); // 該上下文所屬的已經部署了的應用的名字,默認爲"" String getDisplayName(); // 友好的展現名字 long getStartupDate(); // 該上下文第一次加載的時間 ApplicationContext getParent(); // 父級ApplicationContext AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
前四個方法用於獲取該ApplicationContext
的一些基本信息,getAutowireCapableBeanFactory()
用於暴露AutowireCapableBeanFactory
的功能,這一般不是提供給用於代碼使用的,除非你想要在應用上下文的外面初始化bean的實例,關於AutowireCapableBeanFactory
後面會有更加詳細的解析。函數
BeanFactory
是Spring框架中比較重要的一個接口,下面列出了這個接口中的方法的定義:
// 獲取bean Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; // 獲取bean的提供者(對象工廠) <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); boolean containsBean(String name); // 是否包含指定名字的bean boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 是否爲單例 boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 是否爲原型 // 指定名字的bean是否和指定的類型匹配 boolean isTypeMatch(String name, ResolvableType typeToMatch); boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 獲取指定名字的bean的類型 String[] getAliases(String name); // 獲取指定名字的bean的全部別名
這些方法大體能夠分爲三類:
getBean()
方法用於獲取匹配的bean的實例對象(有多是Singleton或者Prototype的),若是沒有找到匹配的bean則拋出BeansException
子類的異常。若是在當前的工廠實例中沒有找到匹配的bean,會在父級的工廠中進行查找。帶有args
參數的getBean()
方法,容許顯式的去指定構造器或者工廠方法的參數,會覆蓋了在bean的定義中定義的參數,這僅僅在建立一個新的實例的時候才起做用,而在獲取一個已經存在的實例是不起做用的。
getBeanProvider()
方法用於獲取指定bean的提供者,能夠看到它返回的是一個ObjectProvider
,其父級接口是ObjectFactory
。首先來看一下ObjectFactory
,它是一個對象的實例工廠,只有一個方法:
T getObject() throws BeansException;
調用這個方法返回的是一個對象的實例。此接口一般用於封裝一個泛型工廠,在每次調用的時候返回一些目標對象新的實例。ObjectFactory
和FactoryBean
是相似的,只不過FactoryBean
一般被定義爲BeanFactory
中的服務提供者(SPI)實例,而ObjectFactory
一般是以API的形式提供給其餘的bean。簡單的來講,ObjectFactory
通常是提供給開發者使用的,FactoryBean
通常是提供給BeanFactory
使用的。
ObjectProvider
繼承ObjectFactory
,特爲注入點而設計,容許可選擇性的編程和寬泛的非惟一性的處理。在Spring 5.1的時候,該接口從Iterable
擴展,提供了對Stream
的支持。該接口的方法以下:
// 獲取對象的實例,容許根據顯式的指定構造器的參數去構造對象 T getObject(Object... args) throws BeansException; // 獲取對象的實例,若是不可用,則返回null T getIfAvailable() throws BeansException; T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException; void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException; // 獲取對象的實例,若是不是惟一的或者沒有首先的bean,則返回null T getIfUnique() throws BeansException; T getIfUnique(Supplier<T> defaultSupplier) throws BeansException; void ifUnique(Consumer<T> dependencyConsumer) throws BeansException; // 獲取多個對象的實例 Iterator<T> iterator(); Stream<T> stream(); Stream<T> orderedStream()
這些接口是分爲兩類,
getIfAvailable()
方法用於獲取可用的bean(沒有則返回null),getIfUnique()
方法用於獲取惟一的bean(若是bean不是惟一的或者沒有首選的bean返回null)。getIfAvailable(Supplier<T> defaultSupplier)
和getIfUnique(Supplier<T> defaultSupplier)
,若是沒有獲取到bean,則返回defaultSupplier提供的默認值,ifAvailable(Consumer<T> dependencyConsumer)
和ifUnique(Consumer<T> dependencyConsumer)
提供了以函數式編程的方式去消費獲取到的bean。stream()
方法返回連續的Stream
,不保證bean的順序(一般是bean的註冊順序)。orderedStream()
方法返回連續的Stream
,預先會根據工廠的公共排序比較器進行排序,通常是根據org.springframework.core.Ordered
的約定進行排序。其餘的是一些工具性的方法:
containsBean(String name)
方法isSingleton(String name)
和isPrototype(String name)
方法isTypeMatch
方法getType(String name)
方法getAliases(String name)
方法或許你已經注意到了,有兩個方法含有類型是ResolvableType
的參數,那麼ResolvableType
是什麼呢?假如說你要獲取泛型類型的bean:MyBean<TheType>
,根據Class
ResolvableType
就能知足此需求,代碼以下:
ResolvableType type = ResolvableType.forClassWithGenerics(MyType.class, TheType.class); ObjectProvider<MyType<TheType>> op = applicationContext.getBeanProvider(type); MyType<TheType> bean = op.getIfAvailable()
簡單的來講,ResolvableType
是對Java java.lang.reflect.Type
的封裝,而且提供了一些訪問該類型的其餘信息的方法(例如父類, 泛型參數,該類)。從成員變量、方法參數、方法返回類型、類來構建ResolvableType
的實例。
ListableBeanFactory
接口有能列出工廠中全部的bean的能力,下面給出該接口中的全部方法:
boolean containsBeanDefinition(String beanName); // 是否包含給定名字的bean的定義 int getBeanDefinitionCount(); // 工廠中bean的定義的數量 String[] getBeanDefinitionNames(); // 工廠中全部定義了的bean的名字 // 獲取指定類型的bean的名字 String[] getBeanNamesForType(ResolvableType type); String[] getBeanNamesForType(Class<?> type); String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit); // 獲取全部使用提供的註解進行標註的bean的名字 String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType); // 查找指定bean中的全部指定的註解(會考慮接口和父類中的註解) <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException; // 根據指定的類型來獲取全部的bean <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException; <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException; // 獲取全部使用提供的註解進行標註了的bean Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
上面的這些方法都不考慮祖先工廠中的bean,只會考慮在當前工廠中定義的bean。
FactoryBean
建立的bean,這也意味着FactoryBean
會被初始化。爲何這裏的三個方法不返回List?Map不光包含這些bean的實例,並且還包含bean的名字,而List只包含bean的實例。也就是說Map比List更加的通用。HierarchicalBeanFactory
接口定義了BeanFactory
之間的分層結構,ConfigurableBeanFactory
中的setParentBeanFactory
方法能設置父級的BeanFactory
,下面列出了HierarchicalBeanFactory
中定義的方法:
BeanFactory getParentBeanFactory(); // 獲取父級的BeanFactory // 本地的工廠是否包含指定名字的bean boolean containsLocalBean(String name);
這兩個方法都比較直接明瞭,getParentBeanFactory
方法用於獲取父級BeanFactory
。containsLocalBean
用於判斷本地的工廠是否包含指定的bean,忽略在祖先工廠中定義的bean。
MessageSource
主要用於消息的國際化,下面是該接口中的方法定義:
// 獲取消息 String getMessage(String code, Object[] args, String defaultMessage, Locale locale); String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException; String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
以上的三個方法都是用於獲取消息的,第一個方法提供了默認消息,第二個接口若是沒有獲取到指定的消息會拋出異常。第三個接口中的MessageSourceResolvable
參數是對代碼、參數值、默認值的一個封裝。
ApplicationEventPublisher
接口封裝了事件發佈功能,提供Spring中事件的機制。接口中的方法定義以下:
// 發佈事件 void publishEvent(ApplicationEvent event); void publishEvent(Object event);
第一個方法用於發佈特定於應用程序事件。第二個方法能發佈任意的事件,若是事件不是ApplicationEvent
,那麼會被包裹成PayloadApplicationEvent
事件。
EnvironmentCapable
提供了訪問Environment
的能力,該接口只有一個方法:
Environment getEnvironment();
Environment
表示當前正在運行的應用的環境變量,它分爲兩個部分:profiles和properties。它的父級接口PropertyResolver
提供了property的訪問能力。
先來看一下ResourceLoader
,該接口是用來加載資源(例如類路徑或者文件系統中的資源)的策略接口。該接口中的方法以下:
Resource getResource(String location); // 根據指定的位置獲取資源 ClassLoader getClassLoader(); // 獲取該資源加載器所使用的類加載器
該接口只有簡單明瞭的兩個方法,一個是用來獲取指定位置的資源,一個用於獲取資源加載器所使用的類加載器。Resource
是從實際類型的底層資源(例如文件、類路徑資源)進行抽象的資源描述符。先看下Resource
中的方法:
boolean exists(); // 資源其實是否存在 boolean isReadable(); // 資源是否可讀 boolean isOpen(); // 檢查資源是否爲打開的流 boolean isFile(); // 資源是否爲文件系統上的一個文件 URL getURL() throws IOException; // 獲取url URI getURI() throws IOException; // 獲取URI File getFile() throws IOException; // 獲取文件 ReadableByteChannel readableChannel() throws IOException; // 獲取ReadableByteChannel long contentLength() throws IOException; // 資源的內容的長度 long lastModified() throws IOException; // 資源的最後修改時間 // 相對於當前的資源建立一個新的資源 Resource createRelative(String relativePath) throws IOException; String getFilename(); // 獲取資源的文件名 String getDescription(); // 獲取資源的描述信息
Resource
的父級接口InputStreamSource
,能夠簡單的理解爲InputStream
的來源,只有一個方法,以下:
InputStream getInputStream() throws IOException; // 獲取輸入流
接下來在來看一下ResourcePatternResolver
,該接口用於解析一個位置模式(例如Ant風格的路徑模式),該接口只有一個方法,以下:
// 將給定的位置模式解析成資源對象 Resource[] getResources(String locationPattern) throws IOException;
假如讓你設計IoC容器,你該如何去作呢?首先你應該要明確你設計的容器的功能和特性,而後根據這些功能和特性設計出合理的接口。下面只是粗略的分析一下: