Spring IOC 學習總結

1 什麼是IOC、DI

  IoC—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。html

  傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對象的建立。java

  IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。mysql

  其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。web

  DI—Dependency Injection,即「依賴注入」:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。spring

  理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:sql

  ●誰依賴於誰:固然是應用程序依賴於IoC容器;編程

  ●爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源;設計模式

  ●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;緩存

  ●注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)。服務器

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

  IoC技術是Spring的核心,對於spring框架來講,就是由spring來負責控制對象的生命週期和對象間的關係。全部的類都會在spring容器中登記,告訴spring你是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象,而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。IoC的一個重點是在系統運行中,動態的向某個對象提供它所須要的其餘對象。這一點是經過DI(Dependency Injection,依賴注入)來實現的。

  因此控制反轉IoC(Inversion of Control)是說建立對象的控制權進行轉移,之前建立對象的主動權和建立時機是由本身把控的,而如今這種權力轉移到第三方,好比轉移交給了IoC容器,它就是一個專門用來建立對象的工廠,你要什麼對象,它就給你什麼對象,有了 IoC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IoC容器了,經過IoC容器來創建它們之間的關係。

  注意,通常將BeanFactory稱爲IoC容器,而稱ApplicationContext和WebApplicationContext爲應用上下文或Web應用上下文。在本篇博客中,把WebApplicationContext和ApplicationContext統稱爲Spring容器。

 

2 IoC相關的Java基礎知識

2.1 反射

  IOC中最基本的技術就是「反射(Reflection)」。反射機制: 反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查而且對內部的成員進行操做。例如它容許一個java的類獲取它全部的成員變量和方法而且顯示出來。經過這種能力能夠完全的瞭解自身的狀況爲下一步的動做作準備。Java的反射機制的實現要藉助於4個類:class,Constructor,Field,Method;其中class表明的時類對象,Constructor-類的構造器對象,Field-類的屬性對象,Method-類的方法對象。經過這四個對象咱們能夠粗略的看到一個類的各個組 成部分。

  Java反射的做用:在Java運行時環境中,對於任意一個類,能夠知道這個類有哪些屬性和方法。對於任意一個對象,能夠調用它的任意一個方法。這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java 語言的反射(Reflection)機制。

  Java 反射機制主要提供瞭如下功能,在運行時判斷任意一個對象所屬的類。在運行時構造任意一個類的對象。在運行時判斷任意一個類所具備的成員變量和方法。在運行時調用任意一個對象的方法。

  從Java內存區域的角度理解反射,首先上一張Java內存區域的神圖:

  

  

  首先咱們瞭解一下JVM,什麼是JVM,Java的虛擬機,java之因此能跨平臺就是由於這個東西,你能夠理解成一個進程,程序,只不過他的做用是用來跑你的代碼的。

  假如你寫了一段代碼:Object o=new Object(); 運行了起來!

  首先JVM會啓動,你的代碼會編譯成一個.class文件,而後被類加載器加載進jvm的內存中,你的類Object加載到方法區中,建立了Object類的class對象到堆中,注意這個不是new出來的對象,而是類的類型對象,每一個類只有一個class對象,做爲方法區類的數據結構的接口。jvm建立對象前,會先檢查類是否加載,尋找類對應的class對象,若加載好,則爲你的對象分配內存,初始化也就是代碼:new Object()。

  上面的流程就是你本身寫好的代碼扔給jvm去跑,跑完就over了,jvm關閉,你的程序也中止了。

  爲何要講這個呢?由於要理解反射必須知道它在什麼場景下使用。你們想一想上面的程序對象是本身new的,程序至關於寫死了給jvm去跑。假如一個服務器上忽然遇到某個請求哦要用到某個類,哎呀但沒加載進jvm,是否是要停下來本身寫段代碼,new一下,哦啓動一下服務器,(腦殘)!

  反射是什麼呢?當咱們的程序在運行時,須要動態的加載一些類這些類可能以前用不到因此不用加載到jvm,而是在運行時根據須要才加載,這樣的好處對於服務器來講不言而喻,舉個例子咱們的項目底層有時是用mysql,有時用oracle,須要動態地根據實際狀況加載驅動類,這個時候反射就有用了,假設 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection這兩個類咱們要用,這時候咱們的程序就寫得比較動態化,經過Class tc = Class.forName("com.java.dbtest.TestConnection");經過類的全類名讓jvm在服務器中找到並加載這個類,而若是是oracle則傳入的參數就變成另外一個了。這時候就能夠看到反射的好處了,這個動態性就體現出java的特性了。

  反射能夠實現動態建立對象和編譯,體現出很大的靈活性(特別是在J2EE的開發中它的靈活性就表現的十分明顯)。經過反射機制咱們能夠得到類的各類內容,進行了反編譯。對於JAVA這種先編譯再運行的語言來講,反射機制可使代碼更加靈活,更加容易實現面向對象。反射機制的缺點:對性能有影響。使用反射基本上是一種解釋操做,咱們能夠告訴JVM,咱們但願作什麼而且它 知足咱們的要求。這類操做老是慢於只直接執行相同的操做。

  在Java程序中,沒有反射的狀況,你須要去new對象,實現對象的組合給容器使用,如今不用了,你遵照規範給個配置文件,容器根據你配置文件就自動完成對象的建立,你在什麼地方用,只須要給個申明,標個註解就行,程序運行的時候容器會自動把已經建立好的對象賦給你那個聲明的引用。這個過程就叫依賴注入,依賴配置文件利用Java反射建立對象,解析你的註解的位置注入對象。

  Spring的IOC正是反射技術最好的舞臺,IOC中大量利用反射技術,經過配置XML或者@Annotation的方式配置裝配bean到Spring容器,程序運行起來後,根據須要,Spring容器在動態的注入bean到須要的對象中。

  那麼,Spring是如何裝載XML文件以及資源文件的呢?在2.2中介紹。

2.2 資源訪問利器:Spring Resource接口

  JDK所提供的訪問資源的類(如java.net.URL、File等)並不能很好地知足各類底層資源的訪問需求,好比缺乏從類路徑或者Web容器的上下文中獲取資源的操做類。鑑於此,Spring設計了一個Resource接口,它爲應用提供了更強的底層資源訪問能力。Spring的各類資源加載,都是經過這個接口來實現的

  Resource接口的主要方法有

    boolean exists():資源是否存在。

    boolean isOpen():資源是否打開。

    URL getURL():若是底層資源能夠表示爲URL,該方法返回對應的URL對象。

    File getFile():若是底層資源對應一個文件,該方法返回對應的File對象。

    InputStream getInputStream() throws IOException:返回資源對應的輸入流。

  爲了能夠在不顯示使用Resource實現類的狀況下,僅經過資源地址的特殊標識就能夠加載相應的資源,Spring提供了一個強大的加載資源的機制,能夠經過"classpath:","file:"等資源地址前綴識別不一樣的資源類型,還支持ant風格的帶通配符的資源地址。

  

 

3 BeanFactory和ApplicationContext

  什麼是POJO,什麼是Java Bean,什麼是Bean?

  1)POJO = "Plain Old Java Object",是MartinFowler等發明的一個術語,用來表示普通的Java對象,我的理解就是含有一些私有屬性,以及getter/setter/constructer方法的普通java類。

  2)Java Bean比POJO要複雜一些,須要知足一些規範,例如:須要實現 Serializable 接口用於實現 Bean 的持久性。

  3)Bean比Java Bean更寬泛一些,全部能夠被Spring容器實例化並管理的Java類均可以稱之爲Bean。

  Spring經過一個配置文件描述Bean及Bean之間的依賴關係,利用Java語言的反射功能實例化Bean並創建Bean之間的依賴關係。Spring的IOC容器在完成這些底層工做的基礎上,還提供了Bean實例緩存、生命週期管理、Bean實例代理、事件發佈、資源裝載等高級服務。

  BeanFactory是Spring框架最核心的接口,它提供了高級Ioc的配置機制。ApplicationContext創建在BeanFactory基礎之上,提供了更多面嚮應用的功能,它提供了國際化支持和框架事件體系,更易於建立實際應用。二者均可稱爲Spring容器。

  兩者的用途,通常能夠進行簡單的劃分:BeanFactory是Spring框架的基礎設施,面向Spring自己;ApplicationContext面向使用Spring框架的開發者,幾乎全部的應用場合均可以直接使用ApplicationContext而非底層的BeanFactory。

3.1 WebApplicationContext

  WebApplicationContext專門爲Web應用準備的,它容許從相對於Web根目錄的路徑中裝在配置文件(web.xml)完成初始化工做。從WebApplicationContext中能夠得到ServletContext的引用,整個Web應用上下文對象將做爲屬性放置到ServletContext中,以便Web應用環境能夠訪問Spring應用上下文。

  在非Web應用的環境下,Bean只有singleton和prototype兩種做用域,在WebApplicationContext中又添加了三個新的做用域:request、session和global session。

  WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所區別,由於WebApplicationContext須要ServletContext實力,也就是說,它必須在擁有Web容器(Tomcat、Jetty等)的前提下才能完成啓動工做。能夠在web.xml中配置自啓動的Servlet或定義Web容器監聽器(ServletContextListner),藉助二者中的任何一個,就能夠完成啓動Spring Web應用上下文的工做。

  自啓動的Servlet啓動WebApplicationContext:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0"
         metadata-complete="true">
  <!--用maven建立的web-app須要修改servlet的版本爲3.1-->
  <!--配置DispatcherServlet-->
  <servlet>
    <servlet-name>seckill-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--
        配置SpringMVC 須要配置的文件
        spring-dao.xml,spring-service.xml,spring-web.xml
        Mybites -> spring -> springMVC
    -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/spring-*.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>seckill-dispatcher</servlet-name>
    <!--默認匹配全部請求-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

  經過Web容器監聽器啓動WebApplicationContext:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:smart-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>smart</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>3</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>smart</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

 

4 Bean做用域

  Scope,也稱做用域,在 Spring IoC 容器是指其建立的 Bean 對象相對於其餘 Bean 對象的請求可見範圍。在 Spring IoC 容器中具備如下幾種做用域:基本做用域(singleton、prototype),Web 做用域(reqeust、session、globalsession),自定義做用域。

    • singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的Bean將只有一個實例

    • prototype:原型模式,每次經過容器的getBean方法獲取prototype定義的Bean時,都將產生一個新的Bean實例

    • request:對於每次HTTP請求,使用request定義的Bean都將產生一個新實例,即每次HTTP請求將會產生不一樣的Bean實例。只有在Web應用中使用Spring時,該做用域纔有效

    • session:對於每次HTTP Session,使用session定義的Bean豆漿產生一個新實例。一樣只有在Web應用中使用Spring時,該做用域纔有效

    • globalsession:每一個全局的HTTP Session,使用session定義的Bean都將產生一個新實例。典型狀況下,僅在使用portlet context的時候有效。一樣只有在Web應用中使用Spring時,該做用域纔有效

  其中比較經常使用的是singleton和prototype兩種做用域。對於singleton做用域的Bean,每次請求該Bean都將得到相同的實例。容器負責跟蹤Bean實例的狀態,負責維護Bean實例的生命週期行爲;若是一個Bean被設置成prototype做用域,程序每次請求該id的Bean,Spring都會新建一個Bean實例,而後返回給程序。在這種狀況下,Spring容器僅僅使用new 關鍵字建立Bean實例,一旦建立成功,容器不在跟蹤實例,也不會維護Bean實例的狀態。

  若是不指定Bean的做用域,Spring默認使用singleton做用域。Java在建立Java實例時,須要進行內存申請;銷燬實例時,須要完成垃圾回收,這些工做都會致使系統開銷的增長。所以,prototype做用域Bean的建立、銷燬代價比較大。而singleton做用域的Bean實例一旦建立成功,能夠重複使用。所以,除非必要,不然儘可能避免將Bean被設置成prototype做用域。

 

5 IoC Bean的4種配置方式比較

類別 基於XML配置 基於註解配置 基於Java類配置 基於Groovy DSL配置
Bean定義 在XML文件中經過<bean>元素定義Bean,如:<bean class="com.xgj.UserDao"/> 在Bean實現類處經過標註@Component或衍型類@Repository、@Service及@Controller定義Bean 在標註了@Configuration的Java類中,經過在類方法上標註@Bean定義一個Bean。方法必須提供Bean的實例化邏輯 在Groovy 文件中經過DSL定義Bean的名稱 ,如 userDao(UserDao)
Bean名稱 經過<bean>的id或name屬性定義,如:<bean id="userDao" class="com.xgj.UserDao"/> 默認名稱爲:com.xgj.userDao#0 經過註解的value屬性定義,如@Component(「userDao」)。默認名稱爲小寫字母打頭的類名(不帶包名):userDao 經過@Bean的name屬性定義,如@Bean(「userDao」),默認名稱爲方法名 經過GroovyDSL定義Bean的名稱
Bean注入 經過<property>子元素或經過p命名空間的動態屬性,如p:userDao-ref=」userDao」進行注入 經過在成員變量或方法入參處標註@Autowired,按類型匹配自動注入。還能夠配合使用@Qualifier按名稱匹配方式注入 比較靈活,能夠經過在方法處經過@Autowired方法入參綁定Bean,而後在方法中經過代碼進行注入,還能夠經過調用配置類的@Bean方法進行注入 比較靈活,能夠在方法出經過ref()方法進行注入,如ref(「logDao」)
Bean生命過程方法 經過<bean>的init-method和destory-method屬性指定Bean實現類的方法名。最多隻能指定一個初始化方法和一個銷燬方法。 經過在目標方法上標註@PostConstruct和@PreDestroy註解指定初始化或銷燬方法,能夠定義任意多個方法 經過@Bean的initMethod或destoryMethod指定一個初始化或銷燬方法.對於初始化方法來講,你能夠直接在方法內部經過代碼的方式靈活定義初始化邏輯 經過bean->bean,initMehtod或者bean.destoryMethod指定一個初始化或者銷燬方法
Bean做用範圍 經過<bean>的scope屬性指定,如:<bean class="com.xgj.UserDao" scope="prototype"/> 經過在類定義處標註@Scope指定,如@Scope(「prototype」) 經過在Bean方法定義處標註@Scope指定 經過bean->bean,scope=」prototype」指定
Bean延遲初始化 經過<bean>的lazy-init屬性指定,默認爲default,繼承於的default-lazy-init設置,該值默認爲false 經過在類定義處標註@Lazy指定,如@Lazy(true) 經過在Bean方法定義處標註@Lazy指定 經過bean->bean.lazyInit-true指定

   我在項目中通常採用「XML+註解」的配置方式,DataSource、JdbcTemplate因爲沒法在類中標註註解,因此經過XML配置方式比較好,命名空間:如aop、context等,只能採用基於XML的配置。Bean的實現類是當前項目開發的,能夠直接在Java類中使用基於註解的配置,例如說Service層實現類能夠標註@Service,Controller實現類標註@Controller,配合@Autowired就能夠很好地使用基於註解的配置進行Bean的定義和注入。

 

6 IoC 在Spring Web中的應用

  我的理解,一個Spring Web應用中,是從web.xml開始加載的,經過WebApplicationContext中加載web.xml能夠得到ServletContext的引用,讀取到web.xml中其餘Spring配置文件的路徑,再經過Spring Resource接口加載配置文件,經過反射機制加載.class(Java字節碼文件),並生成java.lang.Class對象,根據註解掃描或者XML配置實例化、注入Bean到WebApplicationContext(Spring 容器),整個Spring 容器(WebApplicationContext對象)將做爲屬性放置到Web容器(ServletContext)中,以便Web容器能夠訪問Spring容器中的內容。

 

7 Spring IoC的優缺點

  Java SE中告訴咱們的一個定律:全部的對象都必須建立;或者說:使用對象以前必須建立,可是如今咱們能夠沒必要必定遵循這個定律了,咱們能夠從Ioc容器中直接得到一個對象而後直接使用,無需事先建立它們。這種變革,就如同咱們無需考慮對象銷燬同樣;由於Java的垃圾回收機制幫助咱們實現了對象銷燬;如今又無需考慮對象建立,對象的建立和銷燬都無需考慮了,這給編程帶來的影響是巨大的。

優勢:

  Spring經過這種控制反轉(IoC)的設計模式促進了鬆耦合。當應用了IoC,一個對象依靠的其它對象會經過被動的方式傳送進來,而不是這個對象本身建立或者查找依賴對象。不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴注入給它(依賴注入)。咱們能夠把IoC模式看作是工廠模式的昇華,能夠把IoC看做是一個大工廠,只不過這個大工廠裏要生成的對象都是在XML文件中給出定義的,而後利用Java的「反射」編程,根據XML中給出的類名生成相應的對象。從實現來看,IoC是把之前在工廠方法裏寫死的對象生成代碼,改變爲由XML文件來定義,也就是把工廠和對象生成這二者獨立分隔開來,目的就是提升靈活性和可維護性。

  其實控制反轉就是不須要咱們手動new一個對象了,它把咱們所要實例化的對象都寫在了配置文件xml中了,通常這個類都是咱們應用的業務類(Business Object)。框架內部已經將xml中配置的類自動實例化成對象,當咱們調用某個類A,而且這個類中存在另外一個類B時,咱們就說A依賴於B,容器就會將B對象注入到A類中。之前是由類中的代碼查找類並new對象,如今是xml文件控制的對象的生成,控制權由程序代碼轉移到了xml文件中(Spring 容器)。這樣作仍是有好處的,假如在A中須要5個對象,那麼A類中就會new5個對象,無論之後A中用不用到這5個類,只要用到A類,就會把這5個類所有new出來。假如咱們在xml文件中定義類的話,當類須要用到其中的三個類時,就會依賴注入將對象注入進來,不用的就不注入進來,由此看來,第一個方法時將類A和5個類牢牢聯繫起來,無論用不用到5個類都new一下,真浪費,而第二個方法是第一個類你須要個人時候我就注入進來被你用,你不須要就和我不要緊。這樣類A和其中的5個類是分別獨立的互不干預,當有關係的時候,容器自動注入關係。

  簡單來講,使用IoC,能夠靈活提供不一樣的子類實現(其實就是解耦),提升程序的靈活性、可擴展性和可維護性;由Spring容器經過配置文件來統一管理對象的生命週期、依賴關係等。

缺點:

  由於使用反射來建立對象,因此在效率上會有些損耗。但相對於程序的靈活性和可維護性來講,這點損耗是微不足道的。

 

 

 

 

 

參考資料:

https://blog.csdn.net/qq_22654611/article/details/52606960/

https://www.zhihu.com/question/24304289/answer/76541818

https://www.zhihu.com/question/24304289/answer/147529485

https://blog.csdn.net/u011468990/article/details/49995865

http://www.javashuo.com/article/p-kzcfhszm-cx.html

《精通Spring 4.x 企業應用開發實戰》 陳雄華 林開雄 文建國 編著

相關文章
相關標籤/搜索