1. 程式人生 > >Spring IOC(一)概覽

Spring IOC(一)概覽

正文

Spring ioc原始碼解析這一系列文章會比較枯燥,但是隻要堅持下去,總會有收穫,一回生二回熟,沒有第一次,哪有下一次...

本系列目錄:

回到頂部

一、Spring IOC概述

1.1 IOC

Ioc—Inversion of Control,即“控制反轉”,是一種設計思想。在Java開發中,Ioc意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。

  ●誰控制誰,控制什麼:傳統Java SE程式設計,我們直接在物件內部通過new進行建立物件,是程式主動去建立依賴物件;而IoC是有專門一個容器來建立這些物件,即由Ioc容器來控制物件的建立;誰控制誰?當然是IoC 容器控制了物件;控制什麼?那就是主要控制了外部資源獲取(不只是物件包括比如檔案等)。

  ●為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程式是由我們自己在物件中主動控制去直接獲取依賴物件,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴物件;為何是反轉?因為由容器幫我們查詢及注入依賴物件,物件只是被動的接受依賴物件,所以是反轉;哪些方面反轉了?依賴物件的獲取被反轉了。

  用圖例說明一下,傳統程式設計都是主動去建立相關物件然後再組合起來,如下圖:

當有了IoC/DI的容器後,在客戶端類中不再主動去建立這些物件了,如下圖:

1.2 DI

DI—Dependency Injection,即“依賴注入”元件之間依賴關係由容器在執行期決定,形象的說,即由容器動態的將某個依賴關係注入到元件之中

依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,併為系統搭建一個靈活、可擴充套件的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何程式碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。

  理解DI的關鍵是:“誰依賴誰,為什麼需要依賴,誰注入誰,注入了什麼”,那我們來深入分析一下:

  ●誰依賴於誰:當然是應用程式依賴於IoC容器

  ●為什麼需要依賴:應用程式需要IoC容器來提供物件需要的外部資源

  ●誰注入誰:很明顯是IoC容器注入應用程式某個物件,應用程式依賴的物件

  ●注入了什麼:就是注入某個物件所需要的外部資源(包括物件、資源、常量資料)

1.3 IOC和DI關係

  IoC和DI由什麼關係呢?其實它們是同一個概念的不同角度描述,由於控制反轉概念比較含糊(可能只是理解為容器控制物件這一個層面,很難讓人想到誰來維護物件關係),所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IoC 而言,依賴注入”明確描述了“被注入物件依賴IoC容器配置依賴物件”。

回到頂部

二、核心類原始碼解讀

2.1 Spring IOC容器介面設計

Spring框架中,一旦把一個bean納入到Spring IoC容器之中,這個bean的生命週期就會交由容器進行管理,一般擔當管理者角色的是BeanFactory或ApplicationContext。下面來看一下IOC容器介面設計,如下圖(預設JDK8):

如上圖,可見主要有兩條主線

  1.基本容器:BeanFactory-》HierarchicalBeanFactory-》ConfigurableBeanFactory

  • BeanFactory介面定義了基本的Ioc容器的規範,包括getBean()這樣的Ioc容器的基本方法(通過這個方法可以從容器中取得Bean)。
  • HierarchicalBeanFactory增加了getParentBeanFactory()的介面功能,使BeanFactory具備了雙親Ioc容器的管理功能。
  • ConfigurableBeanFactory定義了一些配置功能,比如通過setParentBeanFactory()設定雙親Ioc容器,通過addBeanPostProcessor()配置Bean後置處理器,等等。

  2.高階容器:BeanFactory-》ListableBeanFactory-》ApplicationContext-》WebApplicationContext/ConfigurableApplicationContext

  • ListableBeanFactory細化了許多BeanFactory的介面功能,比如定義了getBeanDefinitionNames()介面方法;
  • ApplicationContext介面,它通過繼承MessageSource、ResourcePatternResolver、ApplicationEventPublisher介面,在BeanFactory簡單Ioc容器的基礎上添加了許多對高階容器的特性支援。

2.2 BeanFactory介面

尊重原始碼,以下摘自BeanFactory原始碼註釋翻譯:

BeanFactory是獲取spring bean容器的頂級介面。該介面被持有一系列bean definitions的物件所實現。依賴bean definitions,工廠返回一個原型例項或者一個單例例項。
通常,BeanFactory將載入儲存在配置中的bean definitions資源(例如XML文件)。這些定義沒有限制何種方式儲存:LDAP, RDBMS, XML,properties file等。並且鼓勵使用bean的依賴注入引用。

實現類需要支援Bean的完整生命週期,完整的初始化方法及其標準順序(格式:介面 方法)為:

1.BeanNameAware setBeanName 設定bean名稱
2.BeanClassLoaderAware setBeanClassLoader 設定bean類載入器
3.BeanFactoryAware setBeanFactory 設定bean工廠
4.EnvironmentAware setEnvironment 設定環境:profiles+properties
5.EmbeddedValueResolverAware setEmbeddedValueResolver 設定嵌入式值解析器
6.ResourceLoaderAware setResourceLoader 設定資源載入器,只適用於在應用程式上下文中執行
7.ApplicationEventPublisherAware setApplicationEventPublisher注入應用事件釋出器ApplicationEventPublisher
8.MessageSourceAware setMessageSource 設定國際化支援
9.ApplicationContextAware setApplicationContext 設定應用上下文
10.ServletContextAware setServletContext 設定servlet上下文
11.BeanPostProcessors postProcessBeforeInitialization 執行bean處理器前置方法
12.InitializingBean afterPropertiesSet 執行初始化Bean設定完屬性後置方法
13.a custom init-method definition 執行自定義初始化方法
14.BeanPostProcessors postProcessAfterInitialization 執行
bean處理器後置方法
銷燬順序:
1.DestructionAwareBeanPostProcessors postProcessBeforeDestruction 銷燬處理器的前置方法
2.DisposableBean destroy Bean銷燬回撥方法
3.a custom destroy-method definition 使用者自定義銷燬方法

關於Spring bean 生命週期的驗證,飛機票Spring bean 生命週期驗證

下面來看一下BeanFactory介面原始碼:

複製程式碼
 1 public interface BeanFactory {
 2 
 3     //轉定義符
 4     String FACTORY_BEAN_PREFIX = "&";
 5 
 6     //定義5種獲取Bean方法
 7     Object getBean(String name) throws BeansException;
 8     <T> T getBean(String name, Class<T> requiredType) throws BeansException;
 9     <T> T getBean(Class<T> requiredType) throws BeansException; 
10     Object getBean(String name, Object... args) throws BeansException;
11     <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
12 
13     //判斷容器是否含有指定名字的Bean
14     boolean containsBean(String name);
15 
16     //查詢指定名字的Bean是否是Singleton型別的Bean.
17     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
18 
19     //查詢指定名字的Bean是否是Prototype型別的
20     boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
21 
22     //查詢指定了名字的Bean的Class型別是否是特定的Class型別.
23     boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
24     boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
25 
26     //查詢指定名字的Bean的Class型別.
27     Class<?> getType(String name) throws NoSuchBeanDefinitionException;
28 
29     //查詢指定了名字的Bean的所有別名,這些都是在BeanDefinition中定義的
30     String[] getAliases(String name);
31 
32 }
複製程式碼

2.3 XmlBeanFactory實現類

spring3.1之後推薦直接使用:DefaultListableBeanFactory+XmlBeanDefinitionReader(第三節有樣例)。雖然這個類已廢棄,但不妨礙我們來簡單理解一下

複製程式碼
 1 @Deprecated
 2 @SuppressWarnings({"serial", "all"})
 3 public class XmlBeanFactory extends DefaultListableBeanFactory {
 4 
 5     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
 6     //載入資源構造
 7     public XmlBeanFactory(Resource resource) throws BeansException {
 8         this(resource, null);
 9     }
10     //通過載入資源和父類的BeanFactory構造
11     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
12         super(parentBeanFactory);
13         this.reader.loadBeanDefinitions(resource);
14     }
15 
16 }
複製程式碼

1.在XmlBeanFactory中例項化了一個XmlBeanDefinitionReader,這個Reader物件就是用來處理以xml形式的持有類資訊的BeanDefinitionl類。

2.BeanDefinitionl資訊封裝成Resource,作為構造入參

3.呼叫reader的loadBeanDefinitions,完成容器的初始化和注入。

2.4 模擬容器獲取Bean

複製程式碼
1 public static void main(String[] args) {
2         //1.容器IOC獲取bean初始化
3         ClassPathResource resource = new ClassPathResource("spring.xml");//載入資源
4         DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
5         XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);//構造reader
6         reader.loadBeanDefinitions(resource);//核心方法,解析bean定義
7         Dao dao = factory.getBean("daoImpl", Dao.class);//IOC容器DefaultListableBeanFactory通過名稱和類class獲取bean單例物件
8         dao.select();//執行Bean例項方法
9 }
spring.xml中就寫一行:定義一個Bean即可
 <bean id="daoImpl" class="spring.aop.xml.dao.impl.DaoImpl" />
複製程式碼 回到頂部

三、總結

本文概述IOC/DI 原理並分析了Spring核心介面設計,最後結合一個簡單例子,模擬了最簡單的容器DefaultListableBeanFactory從xml載入bean定義並生成bean物件的過程讓大家有一個大體的認知。

下一章,我們將分析容器初始化。

=======================

參考

http://www.zzcode.cn/springioc/thread-39.html