Spring框架起步,小白都看得懂(官翻版)!

寫在開頭

本篇章介紹Spring框架的完整地所有技術。html

本人偏心Java Configuration(也是Spring推薦的方式),因此一樣配置的狀況下,將略去XML配置不表,請知悉。java

官翻版本:Spring Framework Core 5.2.5.RELEASEspring

本文會盡可能遵照原文排版,因此某些我以爲不過重要的,我會只加標題,而不加具體內容,並放上連接,供感興趣的讀者自行閱讀。數據庫

由於考慮到考研,項目開發等,因此文章更新會慢一些,若是你喜歡的話,也能夠私信我提醒更新!編程

須要的基礎知識:api

Java SE(Version 11.0+)
Maven(Version 3.62+)
JetBrains IntelliJ IDEA(Version 2019.3.4+)
GitHub
複製代碼

出現的專業詞彙講解

配置元數據:配置源或配置文件的意思。bash

依賴:顧名思義,構建某個實例須要的其餘實例,由於依賴於這個實例,因此叫「依賴」。app

Bean:中文翻譯「豆子,黃豆」,就是一個類實例,只是這個類只有getter/setter方法和私有域而已,常做爲數據載體。框架

DAO: 數據庫訪問類(Data Access Object),就是一類專門用來訪問數據庫的Bean測試

耦合:依賴的意思,A和B耦合性越高,說明它倆互相依賴性越強,離開誰另外一個都不行,軟件設計講究低耦合,是爲了後期維護方便,省得「牽一髮而動全身」。

屬性(Property):屬性,其實就是私有域。

緒論

在Spring Framework中,最重要的莫過於Spring Framework的Inversion Of Control(IOC)控制反轉。本文會先介紹IOC技術,以後是完整且全面的Spring Framework Aspect-Oriented Programming(AOP)面向切面編程。從概念上說,Spring的AOP是很好理解的,同時它也能夠解決企業編程的80%的AOP問題。

Spring整合了AspectJ(一個當前特性最豐富,最成熟的Java企業級AOP實現)。(暗示Spring的AOP很牛逼)

IOC容器

Spring IOC容器和Bean介紹

IOC也做爲Dependency Injection(DI)依賴注入而被人知曉。它是一個定義對象依賴的過程,對象從構造參數,工廠方法參數,屬性值獲取建立此對象須要的依賴(其實就是其餘對象)。

容器就有點秀了!它在對象須要依賴時(須要其餘對象時)主動地注入進去,這個過程本質上經過直接構造類或者相似服務定位模式的機制來取代bean實現對於依賴的實例化或定位過程,「接管了」對象對於依賴的控制,就像「反轉同樣」,因此叫控制反轉(Inversion of Control)。

org.springframework.beans和org.springframework.context這兩個包是Spring Framework IOC容器的基礎包,其中:BeanFactory接口提供了一種高級配置機制,這種機制能夠管理任何類型的對象。ApplicationContext是它的一個子接口,它擁有:

a)更簡單地整合Spring AOP特性。
b)管理資源處理。
c)發佈事件。
d)應用層次的特殊上下文(context),好比在Web應用中的WebApplicationContext。
複製代碼

簡而言之,BeanFactory提供了配置框架和基礎功能,ApplicationContext添加一些企業開發方面的功能。Application是BeanFactory的一個徹底的超集,本章僅僅用它來描述Spring IOC容器,想要了解更多關於BeanFactory,詳見這裏

在Spring裏,構成你應用骨幹的對象實例被稱爲「bean」,bean是啥呢?是一個實例化了的,組裝了的,同時被Spring IOC容器管理的對象。bean僅僅是你應用中的衆多對象之一。全部的Bean以及他們所使用的依賴,都被容器使用配置元數據利用反射管理着。

容器概覽

org.springframework.ApplicationContext接口表明了Spring IOC容器,它負責實例化,配置和裝配bean。容器經過讀取配置元數據來決定對哪一個對象進行實例化,配置,和裝配。元數據以XML, Java註解或Java代碼形式。它讓你能夠清楚地指明構成你應用的對象,以及它們之間豐富的內部依賴關係。

下圖展現了Spring是怎麼工做的:

配置元數據

正如圖所示的那樣,Spring IOC容器使用一個配置元數據構成的表單。這個含有配置元數據的表單,表明着你,也就是此應用的開發者,但願Spring容器應該怎樣實例化,配置和裝配你應用中的Bean組件。

傳統的設置配置元數據的方法是使用簡單直觀明瞭的XML形式,這也是本章主要的配置方法。

P.s.
XML並非惟一的格式,Spring IOC容器已經從之解耦出來了,當下,愈來愈多的開發者使用基於Java註解的配置方法(好比我)。
複製代碼

想要了解更對其餘配置格式?

*基於註解的配置(Spring 2.5引入)

*基於Java的配置(Spring 3.0引入,一些由Spring JavaConfig提供的特性逐漸成了Spring Framework的核心,因此你可使用Java配置爲你的應用提供外部Bean而不是使用XML文件,想使用這些新特性?試試@Configuration, @Bean, @Import, @DependsOn)

Spring配置考慮到容器至少管理一個(一般不少個)Bean定義,Java配置在@Configuration註解修飾的類裏面使用@Bean註解來完成定義這些Bean。

這些Bean定義與實例對象聯繫起來而後修飾你的應用。一般狀況下,你會定義業務層對象,好比數據庫訪問對象(DAO. Data Access Object),表示對象,例如Struts Action實例,基礎結構對象,例如Hibernate SessionFactories,JMS隊列,諸如此類。通常來講,加載域對象是DAO或業務邏輯的職責,因此容器裏面不會有細粒化的域對象。不過,你可使用Spring對於AspectJ的整合來配置IOC容器控制以外建立的對象。詳見

簡單用法:

public class Book {
    // ...
}

@Configuration
public class WebConfig {
    // ...
    @Bean(name = "book")
    public Book book() {
        return new Book();
    }
}
複製代碼

若須要在某個配置類引用其餘的配置類,能夠在類前面加上:

@Import(value = {ConfigA.class, ConfigB.class})
複製代碼

使用容器

經過使用:

T getBean(String name, Class<T> requiredType) 複製代碼

方法,能夠獲取Bean實例。

可是,理想狀況下,你不該該使用getBean()方法,由於這會破壞容器對於對象的自動管理,你可使用諸如@Autowired的註解來在特殊Bean裏面獲取你須要的依賴。(說白了,別有事沒事調用getBean(),測試除外,那否則,你還有IOC容器幫你管理幹啥?直接本身整唄!弟弟!)

Bean概覽

在IOC容器裏面,Bean的定義以BeanDefinition的形式呈現。它們包含如下的元數據:

a)包限定名的類,包含了Bean的實現類。
b)Bean行爲定義組件,定義了Bean在IOC容器裏面的行爲。
c)對於此Bean須要的其餘Bean的引用,這些引用又被稱爲「合做者」或「依賴」。
d)其餘用於設置新建立的Bean的配置,好比超時時間,數據庫鏈接池數量等。
複製代碼

元數據被傳遞到一個配置集合中,以用來表達Bean定義。

具體的Bean配置集合,詳見

ApplicationContext還容許用戶在容器外面自定義Bean。經過ApplicationContext的getBeanFactory()方法獲取,此方法返回BeanFactory的DefaultListableBeanFactory實現,DefaultListableBeanFactory支持經過registerSingleton(..)和registerBeanDefinition(..)來註冊Bean。不過,一般狀況下,應用只能和經過配置元數據定義的Bean協做。

命名Bean

一個Bean一般含有一個或多個惟一標識符。若是不指出bean名字,就會使用默認名(類名首字母小寫),bean命名遵照Java命名規範,首字母小寫+駝峯命名。固然還可使用別名(說真的,通常沒用過)。

實例化Bean

對於XML,應該指出(強制地)其類型。在這種方式下,能夠經過調用類的構造器建立,等同於new一個實例;還能夠調用靜態的工廠方法來建立。使用構造器建立時,容器會本身尋找構造器,因此你就只須要像編寫普通類那樣就好,這裏指出,最好有一個無參構造器。

Spring容器不只僅能夠管理實體類,虛擬類也能夠被其管理,好比,不符合Java Bean規範的數據庫鏈接池。

對於XML配置,使用靜態工廠方法時,class指出工廠類,factory-method指出具體的靜態方法。如果使用實例工廠方法,把class屬性換成factory-bean並指向工廠類就好。

依賴

典型的企業級應用一般不會包含單例對象,即便是最小的應用,它的某個對象也須要別的對象的嵌入才能運行。本節介紹如何經過定義Bean配置來實現把單個的對象組織起來,實現徹底解耦合的應用。

依賴注入

Dependency Injection依賴注入(DI)指的是這樣的一個過程:對象只能經過構造器參數,工廠方法參數,在對象實例被構造以後的屬性值或工廠方法的返回值來定義它們的依賴。容器在建立這些Bean時主動注入這些依賴,這個過程稱爲對於Bean本身控制依賴的獲取,定位這一職責的反轉。

歸功於DI的存在,代碼更加簡潔明瞭,解耦更加高效。Bean不在主動引入他們須要的依賴,因此測試更加容易。介於某些Bean是接口的實現類,因此抹除某些特性並加入新的來測試,模擬都變得那麼簡單!

  1. 基於構造器的DI

基於構造器的DI,由容器調用無參或有參構造器完成。調用靜態工廠方法也能夠起到相同的效果,如下展現一個構造器注入的例子:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
複製代碼
  1. 解析構造參數

這玩意屬於XML的了,說白了就是,構造器參數匹配順序出現歧義時,應顯式地指明順序。在Java Configuration裏面,直接@Autowired就完事了,整這麼多記不住的!

  1. 基於setter的DI

容器在調用過你的無參構造器或無參靜態工廠方法後,調用你的setter方法完成依賴注入。來個例子看看:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
複製代碼

除了以上兩種注入,ApplicationContext還支持在構造器注入以後再次經過調用setter進行注入。

構造器注入和setter注入比較:

在setter方法上使用@Required註解可使此屬性成爲必須注入的依賴。帶有參數驗證的控制器注入是更好的選擇。

Spring 團隊建議使用構造器注入,這樣能夠更大程度上減小組件構建時參數變化帶來的風險,以及非空斷定。長遠角度來講,此方法構建的Bean一般是初始化狀態的完美Bean。過多的構造參數,可能說明你的代碼寫得太垃圾了,重構吧!少年!

setter方法注入,通常適用於可選依賴,這種依賴通常能夠被指定一個默認值,可是,仍是得注意對於它的非空檢查。setter注入的好處在於,後期能夠再次配置或注入依賴,增長了變通性。

使用依賴注入,可讓第三方對象也更加方便的享受到容器管理的便捷,可是一旦第三方類沒有setter方法,就只能經過構造器注入依賴了。

  1. 依賴解析過程

容器進行依賴解析過程以下:

a)經過配置元數據(好比xml文件,Java代碼,註解)來建立和初始化ApplicationContext。
b)對於每一個Bean,它的依賴項都以屬性,構造器參數,靜態工廠方法參數呈現出來,當Bean被建立時,依賴會自動添加進去。
c)每個屬性或構造器參數,都是容器裏面的一個對於其餘Bean的引用,或準備設置的值的實際定義。
d)每個屬性或構造器參數的值,都是從特殊類型轉換到屬性或構造器參數的實際類型以後的值。默認狀況下,Spring能夠把String類型的值轉換成八大基本類型。
複製代碼

在容器建立時,Spring會驗證每一個Bean配置的合法性,Bean屬性在Bean被建立以前,一直不會被設置。單例和預建立的Bean會在容器建立時一同被建立,其他狀況均爲須要建立時纔會建立,由於每次建立都會引起連鎖反應(Bean的依賴被建立,以來的依賴被建立...),因此對於不匹配的解析可能會在稍後出現。

注意避免循環依賴,好比A依賴B,B依賴C,C依賴A。對於這個種狀況,要注意避免,好比經過基於setter的DI來下降依賴。

Spring容器會在加載期間推斷潛在的問題(好比空指針,循環引用)。Spring儘量遲地設置屬性,這代表,可能一開始建立Bean正常,但過會就會拋異常。這也就是爲何ApplicationContext使用預建立做爲默認方式的緣由。在ApplicationContext建立時,以時間和內存換取更加可靠地系統(發現更多潛在的問題),而不是在須要建立時才發現問題,正是Spring採起的方式。固然,你能夠覆寫這個策略,實現單例建立(儘量遲地建立)。

若是啥問題也麼得,Spring就會開始注入Bean了,此時Spring會提早配置好須要建立的Bean所須要的依賴。這意味着,若是A須要B,那麼Spring可能會在初始化A以後,而後配置B,再而後調用A的setter方法來完成DI。換句話說,一個Bean被建立,而後調用它的setter方法注入依賴,而後相關的生命週期方法會被調用。

  1. 依賴注入的例子

XML格式略去不表,相似用@Autowired注入。

依賴和細節配置

正如前面提到的那樣,你能夠經過引用其餘被容器管理着的Bean會內置的值來實現對屬性或構造器參數的設置。

  1. 直接值

就,字面意思,寫在屬性標籤(<property/>)裏面的字符串,可做爲屬性值進行注入,這屬於XML文件了,典型的用法就是MyBatis數據庫配置,略過不表。

  1. 引用其餘的Bean

也是字面意思`,經過引用此容器管理的其餘Bean來設置當前Bean的屬性。

  1. 內部Bean

爲了建立此Bean而建立的一個匿名Bean,就像在類裏面建立了匿名類同樣;沒法被容器管理,訪問,注入,只是一個生成此Bean的輔助Bean。

  1. 集合

集合標籤可用於設置Bean的集合類型,常見的集合都可使用。

  1. Null和Empty的String值

空字符串就是屬性值爲"",若不設置屬性,屬性就爲Null。

  1. 複合屬性名

就也是字面意思,至關於屬性的屬性,給屬性的屬性設值。

使用depends-on

能夠強制此Bean所使用的依賴在初始化此Bean錢被強制初始化。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

// or
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
複製代碼

延遲初始化Bean

是一個屬性,指出是否延遲初始化。

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
複製代碼

容器級別的延遲:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>
複製代碼

自動織入協做器

自動織入的優勢:

a)大大減小指定屬性或構造器參數的須要。
b)當你的應用更新時,自動織入能夠更新配置。這個特性,在開發方面很是有用。
複製代碼

自動織入有四個模式

a)no: 不開啓自動織入,經過引用其餘Bean實現引用依賴。
b)byName: 根據屬性名自動織入
c)byType: 若是容器存在此屬性類型,便織入。
d)constructor: 相似byType,可是隻適用於構造器參數。
複製代碼

自動織入的侷限性和不足:

暫略不表。
複製代碼

去除自動織入中的Bean

暫略不表。
複製代碼

方法注入

背景:大多時的場景,大多數的Bean都是單例的,當一個單例Bean須要和另外一個單例Bean協做時,或一個非單例Bean須要和另外一個非單例Bean合做時,一般你會經過把某個Bean定義成另外一個(Bean)的屬性來處理依賴問題。當Bean生命週期不一致時,嚯嚯,問題來了。假設在A的每一個方法調用上,單例Bean A要使用非單例Bean B。容器只會建立A的實例一次,因此只有一次設置屬性B的機會,可是容器可不能在每次須要B的時候都提供一個新的實例啊!這就形成了問題。

解決措施之一是,暫時取消一些IOC,你能夠經過實現ApplicationContextAware接口來讓A發現容器,並經過調用容器的getBean("B")方法來在每次須要B實例時,獲取一個新的實例。來看一個例子:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext( ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
複製代碼

以上代碼耦合到了Spring框架,因此不是很推薦,可是這種方法確實可讓你寫出乾淨的處理代碼。

下一篇

相關文章
相關標籤/搜索