理解Spring(一):Spring 與 IoC

什麼是 Spring

Spring 是一個輕量級的企業級應用開發框架,它於2004年發佈第一個版本,其目的是用於簡化企業級應用程序的開發。java

在傳統應用程序開發中,一個完整的應用是由一組相互協做的對象組成,開發一個應用除了要開發業務邏輯以外,更多的是關注如何使這些對象協做來完成所需功能,並且要高內聚,低耦合。雖然一些設計模式能夠幫咱們達到這個目的,但是這又徒增了咱們的負擔。若是能經過配置的方式來建立對象,管理對象之間依賴關係,那麼就可以減小許多工做,提升開發效率。Spring 框架主要就是來完成這個功能的。spring

Spring 框架除了幫咱們管理對象及其依賴關係以外,還提供了面向切面編程的能力,在此基礎上,容許咱們對一些通用任務如日誌記錄、安全控制、異常處理等進行集中式管理,還能幫咱們管理最頭疼的數據庫事務。此外,它還提供了粘合其餘框架的能力,使咱們能夠方便地與各類第三方框架進行集成,並且不論是 Java SE 應用程序仍是 JavaEE 應用程序均可以使用這個平臺進行開發。數據庫

Spring 是基於 IoCAOP 兩大思想理論衍生而來的,能夠說,Spring是一個同時實現了 IoC 和 AOP 的框架。編程

Spring 的總體架構

Spring 的總體架構如圖所示:設計模式

核心模塊只有3個:Beans、Core、和 Context ,它們構建起了整個 Spring 的骨架,沒有它們就不可能有 AOP、Web 等上層的特性功能。若是在它們3箇中選出一個最核心的模塊的話,那就非 Beans 模塊莫屬了,其實 Spring 就是面向 Bean 的編程(BOP, Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。關於 Bean 的觀念,會在後面進行介紹。安全

什麼是 IoC

IoC(Inverse Of Control,控制反轉)是一種設計思想,目標是實現解耦。所謂控制反轉,是指對資源的控制方式反轉了。這裏說的資源主要指咱們的業務對象,對象之間每每會存在某種依賴關係,當一個對象依賴另外一個對象時,傳統的作法是在它內部直接 new 一個出來,即由對象本身負責建立並管理它所依賴的資源,這是傳統的對資源的控制方式。IoC 就是將其顛倒過來,對象由主動控制所需資源變成了被動接受,由第三方(IoC 容器)對資源進行集中管理,對象須要什麼資源就從IoC容器中取,或者讓容器主動將所需資源注入進來。數據結構

IoC 以後,對象與所需資源之間再也不具備強耦合性,資源能夠被直接替換,而無需改動需求方的代碼。舉個例子,董事長鬚要一個祕書,傳統的作法是,董事長本身去指定一個祕書,控制權在他本身手上,可是這會致使他與祕書之間的耦合性較強,一旦想換祕書了,就得修改本身的代碼。IoC 的作法是,董事長聲明本身須要一個祕書,由IoC 容器爲他指定一個祕書,至因而哪一個祕書,男的仍是女的,一切由容器說了算,若是要換祕書,也是修改容器的配置文件,與董事長無關,這樣就實現了二者間的解耦。架構

IoC 的兩種實現方式:框架

  • DI(Dependency Injection,依賴注入)。所謂依賴注入,是指對象所依賴的資源經過被動注入的方式獲得,換言之,容器會主動地根據預先配置的依賴關係將資源注入進來。
  • DL(Dependency Lookup,依賴查找)。依賴查找是早先的一種 IoC 實現方式,現已過期。對象須要調用容器的API查找它所依賴的資源。

Bean 的概念

  • 在 Java 中,「Bean」是對用Java語言編寫的「可重用組件」的慣用叫法。能夠從字面意思去理解,Java 本來指爪哇咖啡,bean 指咖啡豆,而咖啡的「可重用組件」就是咖啡豆嘛。官方並無說明所謂的「組件」具體指的是什麼,由於「組件」自己就是一個抽象的概念,是對軟件組成部分的抽象,所以,Bean做爲可重用組件的代稱,既可指類,也可指對象。
  • 在 Spring 中,Bean 的概念同上,有時也稱 Component。 由 Spring 的 IoC容器所管理的 Bean 稱做 Spring Bean。
  • 擴展:

    Java Bean 的概念不一樣於 Bean,Java Bean 是指符合 JavaBeans 規範的一種特殊的 Bean,即:全部屬性均爲 private,提供 getter 和 setter,提供默認構造方法。JavaBean 也能夠認爲是遵循特定約定的POJO。post

    POJO(Plain Ordinary Java Object)是指簡單和普通的 Java 對象。嚴格來講,它不繼承類,不實現接口,不處理業務邏輯,僅用於封裝數據。

Spring 的基本使用

首先配置 Bean 信息,向 Spring 的 IoC 容器(或簡稱 Spring 容器)中註冊 Bean。以 XML 方式爲例,以下配置了兩個 Bean,其中第一個依賴第二個:

<bean id="John" class="Person">
	<property name="lover">
		<ref bean="Mary"/>
	</property>
</bean>
<bean id="Mary" class="Person"/>

而後建立 Spring 容器,同時綁定配置文件。以下:

ApplicationContext container = new ClassPathXmlApplicationContext("bean-config.xml");

而後經過容器的 getBean 方法便可獲得咱們在配置文件中所配置的 Bean 的實例。以下:

Person John = container.getBean("John");

Spring 的兩種 IoC 容器

Spring 提供了兩種 IoC 容器: BeanFactory 和 ApplicationContext 。

  • BeanFactory 提供基本的 IoC 服務支持。
  • ApplicationContext 對 BeanFactory 進行了擴展與加強,除了擁有 BeanFactory 的全部能力外,還提供了許多高級特性,如事件發佈、資源加載、國際化消息等。ApplicationContext 接口繼承了 BeanFactory 接口,它的實現類中也是直接複用了 BeanFactory,所以能夠說,ApplicationContext 是 BeanFactory 的加強版。

二者在覈心功能上的區別主要是默認的加載策略不一樣,這點區別幾乎能夠忽略不計,一般狀況下,咱們老是使用更爲強大的 ApplicationContext,不多會直接使用 BeanFactory。

如下是幾個最經常使用的 ApplicationContext 實現類:

  • ClassPathXmlApplicationContext
  • AnnotationConfigApplicationContext
  • AnnotationConfigWebApplicationContext

Spring 容器的基本工做原理

既然是容器,那它最底層確定是一個數據結構。經過跟蹤 getBean 方法,咱們發現它是從一個叫做 singletonObjects 的 Map 集合中獲取 Bean 實例的。singletonObjects 的定義以下:

能夠判定,它就是 Spring 容器的核心,凡是做用域爲單例的 Bean 的實例都保存在該 Map 集合中,我把它稱之爲單例池

那麼 getBean 方法作了哪些事情呢?

getBean 方法首先會從單例池中獲取 Bean 實例,若是取到了就直接返回,不然,若是有父容器,嘗試從父容器中獲取,若是也沒獲取到,則建立實例。建立實例以前先確保該 Bean 所依賴的 Bean 所有初始化,而後,若是是原型 Bean,建立好實例後直接返回,若是是單例 Bean,建立好實例後將其放進單例池,而後再從單例池中獲取並返回。

當 Spring 容器被建立時,它又是如何完成初始化的呢?

ClassPathXmlApplicationContext爲例,它的構造方法主要作的事情就是調用 refresh() 方法。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 準備好本身
        prepareRefresh();
        // 建立並初始化BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 準備好要使用的BeanFactory
        prepareBeanFactory(beanFactory);
        try {
            // 對BeanFactory進行後置處理
            postProcessBeanFactory(beanFactory);
            // 調用BeanFactory的後置處理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // 註冊Bean的後置處理器
            registerBeanPostProcessors(beanFactory);
            // 初始化消息源
            initMessageSource();
            // 初始化事件多播器
            initApplicationEventMulticaster();
            // 初始化其餘特殊的bean
            onRefresh();
            // 檢測並註冊監聽器Bean
            registerListeners();
            // 實例化其他全部(非懶加載)的單例Bean
            finishBeanFactoryInitialization(beanFactory);
            // 最後一步:發佈相應的事件
            finishRefresh();

refresh() 方法的主要執行流程:

  1. 調用 refreshBeanFactory() 方法,該方法會首先新建或重建 BeanFactory 對象,而後使用相應的BeanDefinitionReader 讀取並解析 Bean 定義信息,將每一個 Bean 定義信息依次封裝成 BeanDefinition 對象,並將這些 BeanDefinition 對象註冊到 BeanDefinitionRegistery。這一步,完成了 BeanFactory 的建立,以及 Bean 定義信息的加載。
  2. 配置 BeanFactory,對 BeanFactory 作一些後置處理,註冊 Bean 的後置處理器,初始化消息源和事件多播器,註冊監聽器等。這一步,完成了 Spring 容器的配置工做。
  3. 調用 finishBeanFactoryInitialization() 方法,該方法會遍歷以前註冊到 BeanDefinitionRegistery 中的全部 BeanDefinition ,依次實例化那些非抽象非懶加載的單例 Bean,並將其加入單例池。這一步,完成了 Bean 的實例化。

Spring 容器的幾個核心類:

  • DefaultListableBeanFactory 是一個通用的 BeanFactory 實現類,它還同時實現了BeanDefinitionRegistry 接口。從 ApplicationContext 實現類的源碼中能夠看到,在它內部維護着一個 DefaultListableBeanFactory 的實例,全部的 IoC 服務都委託給該 BeanFactory 實例來執行。
  • BeanDefinitionRegistry 負責維護 BeanDefinition 實例,該接口主要定義了registerBeanDefinition()getBeanDefinition() 等方法,用於註冊和獲取 Bean 信息。
  • BeanDefinition 用於封裝 Bean 的信息,包括類名、是不是單例Bean、構造方法參數等信息。每個 Spring Bean 都會有一個 BeanDefinition 的實例與之相對應。
  • BeanDefinitionReader 負責讀取相應配置文件中的內容並將其映射到 BeanDefinition ,而後將映射後的 BeanDefinition 實例註冊到 BeanDefinitionRegistry,由 BeanDefinitionRegistry 來保管它們。

Spring Bean 的註冊與裝配

我的理解,註冊與裝配是不一樣的兩個過程。註冊指的是將 Bean 歸入 IoC 容器。裝配指的是創建 Bean 之間的依賴關係。

Bean 的註冊方式有如下三種:

  • 在 XML文件中配置
  • 在 JavaConfig 中配置
  • 使用@ComponentScan@Component等註解進行配置

Bean 的裝配分爲手動裝配和自動裝配。

手動裝配一樣有三種方式:

  • 在 XML文件 中配置
  • 在 JavaConfig 中配置
  • 使用 @Resource 等註解來配置。
    • 這種方式既能夠算做手動裝配,也能夠算做自動裝配。當咱們在 @Resource 註解中明確指定注入哪個 Bean 時,咱們稱這是手動裝配,而當咱們不進行指定時,則算做自動裝配。

自動裝配也稱自動注入,有兩種開啓方式:

  • 開啓粗粒度的自動裝配,即開啓 Bean 的默認自動裝配。在<bean>標籤中配置default-autowire屬性,或在@Bean註解中配置autowire屬性。開啓了默認自動裝配的 Bean,Spring 會對它的所有屬性都嘗試注入值,這種方式不安全,所以不多使用。
  • 開啓細粒度的自動裝配,即在組件類中使用@Autowired等註解對單個屬性開啓自動裝配。

Spring 支持如下四種用於自動裝配的註解:

  • Spring 自帶的 @Autowired 註解
  • JSR-330 的 @Inject 註解
  • JSR-250 的 @Resource 註解
  • Spring 新增的 @Value 註解,用於裝配 String 和基本類型的值。@Value註解常常配合 SpEL 表達式一塊兒使用。

SpEL 表達式的主要語法:

  • ${},表示從 Properties 文件中讀取相應屬性的值。一般須要同@PropertySource註解配合使用,該註解用於指定從哪一個 Properties 文件中讀取屬性。
  • #{},表示從 Spring Bean 中讀取相應屬性的值。如,#{user1.name}表示從名稱爲 user1 的 Bean 中 讀取 name 屬性值。
  • 冒號:用於指定默認值,如${server.port:80}。

Spring Bean 的做用域與生命週期

未完待續

相關文章
相關標籤/搜索