Spring系列__02IOC簡介

Spring的兩大核心功能就是IoC和AOP,這篇文章主要介紹IoC。 簡單來講,在面向對象思想下,A類中有一個B類的屬性, 那麼咱們在建立A類時每每須要同時建立一個B類的對象,以便A類對其進行調用。可是,這樣的後果即是,A類和B類的耦合度太高。而所謂的IoC(控制反轉),其核心是DI,旨在提供一種更簡單的機制來設置組件依賴項(一般稱爲對象的協做者),並在整個生命週期中管理這些依賴項。IoC一般能夠分爲兩種子類型:依賴注入和依賴查找。java

依賴注入和依賴查找的介紹

依賴查找是一種更傳統的方法,有兩種類型:

  • 依賴拉取(dependency pull,DL) 在依賴拉取中,根據須要從註冊表中提取依賴項,在IoC中,每每是使用JNDI查找來獲取依賴項。
  • 上下文依賴查找(contextualized dependency lookup, ontextualized dependency lookup, CDL) 上下文查找和依賴拉取有些類似,可是不一樣之處就是,查找是針對管理資源的容器執行的,而不是來自某個中央註冊表。 當容器準備將依賴傳遞給組件時,會依次調用每一個組件的performLookup()方法,而後,組件可使用Contaioner接口來查找所須要的依賴項。

依賴注入是目前被普遍採用的新方案,也有兩種常見的風格

  • 構造函數 當在組件的構造函數中提供依賴項的時候,就會發生構造函數依賴注入。 首先,組件聲明一個或者一組構造函數,並將其做爲依賴項做爲參數; 而後在組件實例化的時候有IoC容器將依賴項傳遞給組件。以下代碼所演示:
public class ConstuctorInjecttion {
    private Dependency dependency;
    public ConstructorInjection(Dependency dependency) {
        this.dependency = dependency;
    }
}
複製代碼
構造函數注入屬於強制性的依賴注入,若是沒有依賴項的時候,就不能建立對象;所以,必須有依賴項。
複製代碼
  • setter方法注入 在setter依賴注入中,IoC容器經過使用JavaBean樣式的setter方法注入組件的依賴項。組件的setter方法公開了IOC容器能夠管理的依賴項。示例以下:
public class SetterInjection {
    private Dependency dependency;
    
    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    } 
}
複製代碼
Setter注入屬於可選依賴注入,能夠在沒有依賴項的狀況下建立對象(調用默認的無參構造器), 而後經過調用setter方法來提供依賴項。
複製代碼

查找和注入的比較總結

- 查找:
1.依賴拉取代碼必須主動得到對註冊表的依賴選項;
2.難以獨立於容器進行測試。
- 注入:
1.對代碼沒有任何影響;
2.能夠獨立於容器進行測試。
3.開發更加自由。
複製代碼

setter注入和構造器注入比較總結

- 構造器注入:當對依賴項的依賴關係爲強制依賴的時候,推薦使用這種方式,經過調用對應的帶參數構造器來完成依賴的注入。
- setter方法注入:最簡單的注入方式。當對依賴項的依賴關係爲可選的時候,推薦使用這種方式。經過反射調用無參構造器來實例後,再調用對應的setter方法。這種方式的最大的好處就是:侵入性最小。
複製代碼

Spring中的控制反轉

對於控制反轉,Spring的實現核心是基於依賴注入,而Spring的依賴注入的核心是BeanFactory接口。BeanFactory負責管理組件,包括依賴項以及他們的生命週期。可是,在實際的使用中,Beanfactory的使用並非不少,一般使用其擴展:ApplicationContext,其是BeanFactory的子接口。後面會有詳細介紹。
複製代碼

1.bean的配置

1.配置形式:

有三種配置形式,可是一般說兩種:xml文件和註解,(有些書籍上說第三種是自動裝配)。本文中主要介紹xml形式進行bean的配置。程序員

2.配置方式:

bean的配置方式有三種:spring

  • 經過反射的方式進行配置: 該方式會經過反射機制調用無參構造器來建立對象實例,可是,如果沒有無參構造器,則會拋出異常。
<!--
        bean的配置方式1:經過全類名方式(反射)
        id:IOC容器中惟一存在,若不指定會默認使用全類名
    -->
    <bean id="car" class="com.spring.demo.bean.Car">
 <!--
複製代碼
  • 經過工廠方法(靜態工廠、實例工廠): 該方法會運用了工廠模式的思想,經過調用靜態工廠、實例工廠的方法來建立bean的實例。
<!--
       bean的配置方式2:經過靜態工廠方法
       id 屬性:指定 bean 的 id,用於從容器中獲取
       class 屬性:指定靜態工廠的全限定類名
       factory-method 屬性:指定生產對象的靜態方法
    -->
        <bean id="carByFactory" class="com.spring.demo.bean.StaticFactory"
              factory-method="getCarBean">

        </bean>


    <!--
         bean的配置方式2:經過實例工廠方法
        此種方式是:先把工廠的建立交給 spring 來管理,而後在使用工廠的 bean 來調用裏面的方法
        factory-bean 屬性:用於指定實例工廠 bean 的 id。
        factory-method 屬性:用於指定實例工廠中建立對象的方法。
  -->
        <bean id="instanceFactory" class="com.spring.demo.bean.InstanceFactory"></bean>
        <bean id="carByInstance" class="com.spring.demo.bean.Car" factory-bean="instanceFactory"
              factory-method="getCarByInstance">
            <property name="brand" value="大衆"/>
        </bean>
複製代碼
  • 經過Spring提供的FactoryBean進行配置(使用較少,暫不介紹)。

3.依賴注入的方式:屬性注入、構造器注入

依賴注入: Dependency Injection。 它是 spring 框架核心 ioc 的具體實現。咱們的程序在編寫時, 經過控制反轉, 把對象的建立交給了 spring,可是代碼中不可能出現沒有依賴的狀況。IoC 解耦只是下降他們的依賴關係,但不會消除。 例如:咱們的業務層仍會調用持久層的方法。那這種業務層和持久層的依賴關係, 在使用 Spring 以後, 就讓 Spring 來維護了。簡單的說,就是坐等框架把持久層對象傳入業務層,而不用咱們本身去獲取。 依賴注入的方式主要有兩種:數組

  • 屬性注入就是調用setter方法進行屬性注入,;
  • 構造器注入方式是調用重載的帶參構造器。 說到這裏,能夠來一些代碼了:
<!--
        依賴注入方式1:屬性注入,屬性注入是經過setter方法注入bean的屬性值或者依賴的對象。
        也是實際應用中最經常使用的方式。
        name指定屬性值,value或者value節點指定屬性值,ref指定依賴的對象
    -->
        <property name="company" value="大衆"/>
        <property name="brand" value="寶來"/>
        <property name="price" value="200000.00"/>
        <property name="maxSpeed" value="200"/>
    </bean>

    <!--
       依賴注入方式2:構造器注入
       經過構造方法注入屬性,可以保證bean在實例化後就能使用
       沒有name屬性,默認按照構造器的變量順序加載,也能夠經過index或者type進行限制
       index:指定參數在構造函數參數列表的索引位置
       type:指定參數在構造函數中的數據類型
   -->
    <!--
       若一個 bean 有多個構造器, 如何經過構造器來爲 bean 的屬性賦值
       能夠根據 index 和 value 進行更加精確的定位. (瞭解)
       也能夠經過name來具體制定屬性名進行綁定
       若字面值中包含特殊字符, 則可使用 DCDATA 來進行賦值. (瞭解)
       使用構造器方式注入屬性值時,可使用type或index來區別重載構造器
   -->
    <bean id="car1" class="com.spring.demo.bean.Car">
        <constructor-arg value="寶馬" type="java.lang.String"/>
        <constructor-arg value="mini" type="java.lang.String"/>
        <constructor-arg value="200000.00" type="double"/>
        <constructor-arg value="240" type="int"/>
    </bean>
    <!--
        依賴注入方式3:工廠方法注入
        由於這種方法比較少使用,這裏只是寫一下注釋,沒有demo
     -->
複製代碼

4.IOC容器BeanFactory和ApplicationContext簡介

BeanFactory是Spirng框架的基礎設施,面向Spring自己;ApplicationContext是BeanFactory的子接口,面向Spring框架的應用者,其提供了更多的功能。 ApplicationContext具備兩個只要的實現類:ClasspathXmlApplicationContext和FileSystemXmlApplicationContext;前者經過在類路徑下加載配置文件來完成bean的裝配,後者從文件系統下加載配置文件。項目結構以下所示: ![](img2018.cnblogs.com/blog/163258…bash

BeanFactory 和 ApplicationContext 的區別:建立對象的時間點不同。

  • ApplicationContext:只要一讀取配置文件,默認狀況下就會建立對象。
  • BeanFactory:什麼使用何時建立對象。

ApplicationContext 接口的實現類

  • ClassPathXmlApplicationContext:它是從類的根路徑下加載配置文件 推薦使用這種
  • FileSystemXmlApplicationContext:它是從磁盤路徑上加載配置文件,配置文件能夠在磁盤的任意位置。
  • AnnotationConfigApplicationContext:當咱們使用註解配置容器對象時,須要使用此類來建立 spring 容器。它用來讀取註解。 在這裏,則能夠進行測試了:
@Test
    public void teStetter() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-ioc.xml");
        Car car = (Car) context.getBean("car");
        System.out.println(car);
    }
複製代碼

5.字面值

字面值能夠用value標籤或者value屬性進行注入。基本類型及其包裝類、String等都是採用字面值的方式進行注入。當具備特殊字符的時候,須要特殊處理:session

<!--
        value簡介:
            基本類型及其包裝類、String均可以經過value標籤或者value屬性注入,
            當字面值包含特殊字符的時候,可用<![CDATA[]]>包含
     -->
    <bean id="car2" class="com.spring.demo.bean.Car">
        <property name="company" value="大衆"/>
        <property name="brand">
            <value><![CDATA[<DZ寶來>]]></value>
        </property>
        <property name="maxSpeed" value="200"/>
        <property name="price" value="200000.00"/>
    </bean>
複製代碼

spring中某一屬性不進行顯式聲明時,其會採用默認值,固然你能夠顯式的注入null:適應null標籤 同時,spring支持級聯屬性的注入。框架

6.引用其餘bean

當須要引用其餘的bean時(A類引用B類),能夠經過ref標籤或者ref屬性進行注入。其用法和value相似。泣別:value注入的是字面值,ref注入的是一個引用。固然,內部bean的注入也是這種方式。 示例以下:函數

<!--
        引入其餘bean:
            例如person有car屬性,須要引入,這是就不是使用value屬性了,
            而是使用ref指定須要引入的bean
            也能夠聲明一個內部bean,此時內部bean就無需再指定id了,道理和內部類類似
    -->
    <bean id="person" class="com.spring.demo.bean.Person">
        <property name="name" value="麗麗"/>
        <property name="age" value="23"/>
        <property name="car" ref="car"/>
    </bean>

    <!-- innerBean -->
    <bean id="person1" class="com.spring.demo.bean.Person">
        <property name="name" value="麗麗"/>
        <property name="age" value="23"/>
        <property name="car">
            <bean class="com.spring.demo.bean.Car">
                <property name="brand" value="BMW"/>
                <property name="company" value="BMW"/>
                <property name="maxSpeed" value="233"/>
                <property name="price" value="233333.33"/>
            </bean>
        </property>
    </bean>
複製代碼

7.集合屬性

在Spring中能夠經過xml標籤配置集合屬性:測試

1.list和數組

配置java.util.LIst和數組類型時,能夠經過標籤來配置,list中的屬性可使用value標籤或者ref標籤。 可使用utility scheme來單獨定義集合,這樣的好處就是方便其餘bean進行引用。ui

2.set

set的狀況和list類似,使用set標籤,用法和list標籤同樣。

<!--
        集合屬性:
            list標籤:數組和list數據使用這個標籤,
            set標籤:set數據使用,使用方法同list標籤
    -->
    <bean id="stu" class="com.spring.demo.bean.Student">
        <property name="name" value="蘇大強"/>
        <property name="age" value="60"/>
        <property name="cars">
            <list>
                <ref bean="car"/>
                <ref bean="car1"/>
                <ref bean="car2"/>
                <ref bean="car3"/>
            </list>
        </property>
    </bean>

    <!--
        也能夠單獨定義list,而後直接綁定
    -->
    <util:list id="carList">
        <ref bean="car"/>
        <ref bean="car1"/>
        <ref bean="car2"/>
        <ref bean="car3"/>
    </util:list>

    <bean id="stu1" class="com.spring.demo.bean.Student">
        <property name="name" value="蘇大強"/>
        <property name="age" value="60"/>
        <property name="cars" ref="carList"/>
    </bean>
複製代碼

3.map和properties

java.util.Map能夠經過map標籤進行指定,map標籤中有多個entity子標籤,每一個entity定義一對鍵值對。 在entity中能夠經過key屬性或者key標籤來指定鍵,同理可使用value、ref、bean、null屬性或者標籤指定值。

<!--
        map:
            map的使用可list類似,使用entry標籤來保存每一對鍵值對,
            同時可使用key屬性或者key標籤來存儲key,
            value值或value屬性綁定普通的字面值,ref綁定其餘bean做爲值
    -->
    <bean id="teacher" class="com.spring.demo.bean.Teacher">
        <property name="name" value="張莉"/>
        <property name="age" value="24"/>
        <property name="carMap">
            <map>
                <entry key="car" value-ref="car"/>
                <entry key="car1" value-ref="car1"/>
                <entry key="car2" value-ref="car2"/>
                <entry key="car3" value-ref="car3"/>
            </map>
        </property>
    </bean>
複製代碼

props標籤用來定義java.util.Properties,該標籤有子標籤prop,具體使用方法與map同樣。

8.p命名空間

Spring2.5以後支持p命名空間,能夠簡化xml配置,屬性與原來的配置同樣:

<!--
        spring2.5以後,可使用p命名空間來簡化屬性配置,須要在配置文件中引入p命名空間
       xmlns:p="http://www.springframework.org/schema/p"
    -->
    <bean id="car4" class="com.spring.demo.bean.Car" p:brand="寶馬" p:company="寶馬"
          p:price="2000000.00" p:maxSpeed="200"/>
複製代碼

8.Java配置

前面的例子都是經過xml文件進行bean的配置,如今來示範一下經過Java配置方式來配置。

@Configuration   //該註解表示這個類是一個配置類,其做用至關於配置文件。
public class CarConfigure {
    @Bean    //該註解表示會向容器中配置一個bean,其value屬性是設置bean的id,
                    //沒有的話默認使用方法名做爲id,bean的類型就是返回值類型
    public Car carByAnnotation() {
        return new Car();
    }
}
複製代碼

9.autowire

Spring的IOC容器能夠經過自動裝配來配置bean,使用方式就是使用autowire屬性,有兩種模式:byName和byType。 byName會在Spring容器中根據名字取尋找匹配的bean,沒有的話就沒法完成裝配; byType會在Spring容器中按照類型來查找並進行bean的配置,可是當找到多個符合條件的類型的bean時會報異常,不過能夠經過primary屬性或註解來制定bean的優先級,可是當你設置了多個同一類型的bean的primary屬性爲true的時候失效;這個時候,能夠藉助@Qualifier註解來加以限制。

<!-- 自動裝配: 只聲明 bean, 而把 bean 之間的關係交給 IOC 容器來完成 -->
    <!--
        byType: 根據類型進行自動裝配. 但要求 IOC 容器中只有一個類型對應的 bean, 如有多個則沒法完成自動裝配.
        byName: 若屬性名和某一個 bean 的 id 名一致, 便可完成自動裝配. 若沒有 id 一致的, 則沒法完成自動裝配
    -->
    <!-- 在使用 XML 配置時, 自動轉配用的很少. 但在基於 註解 的配置時, 自動裝配使用的較多.  -->
    <bean id="car" class="com.spring.demo.bean.Car">
        <property name="company" value="大衆"/>
        <property name="brand" value="寶來"/>
        <property name="price" value="200000.00"/>
        <property name="maxSpeed" value="200"/>
    </bean>

    <bean id="person" class="com.spring.demo.bean.Person" autowire="byName">
        <property name="name" value="James"/>
        <property name="age" value="23"/>
        <property name="car" ref="carSub"/>
    </bean>

   <!-- <bean id="person1" class="com.spring.demo.bean.Person" autowire="byType">
        <property name="name" value="James"/>
        <property name="age" value="23"/>
    </bean>-->

    <!--
        自動裝配的缺點:
            1.autowire默認會爲bean裝配所有屬性,因此當你只想裝配部分屬性時顯得不夠靈活;
            2.byName和byType兩種模式只能選擇一個,不能兼有。
            
    -->

 @Test
    public void testByName() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }

    @Test
    public void testByType() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
        Person person = (Person) context.getBean("person1");
        System.out.println(person);
    }
複製代碼

10.bean的繼承與依賴

做爲一個Java程序員,確定會接觸到繼承。Spring中也提供了bean的繼承,思想和麪向對象的繼承的思想類似。區別在於Spring中的bean的繼承是指bean的配置能夠被繼承(複用)。

<!--
        bean的繼承:
            你能夠定義一個bean並有其餘bean繼承,這樣作的好處是能減小配置
            父bean的配置會由子bean繼承,固然部分屬性除外(autowire,abstract)
            子bean能夠從新賦值一些屬性,進行bean從新定義
            父bean能夠定義爲abstract,定義爲abstract的bean只能被繼承,不能被註冊(實例化)
            父bean能夠不定義class屬性,由子類本身定義
            子bean須要經過parent屬性指定你繼承的父bean
    -->
    <bean id="carSup" class="com.spring.demo.bean.Car" p:company="寶馬" p:brand="mini"
          p:maxSpeed="270" p:price="300000.00" abstract="true"/>

    <bean id="carSub" parent="carSup"/>

 @Test
    public void testInherit() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
        Car car = (Car) context.getBean("carSub");
        System.out.println(car);
    }
複製代碼

在本文的開頭,咱們引入了A類和B類,當你想建立A類時,而A類又必須有B類的變量時,即可以使用依賴來解決這件事:

<!--
        bean的依賴:
            你能夠聲明當前bean依賴於哪些bean,這樣當前bean初始化以前會去初始化依賴的bean,
            若是沒有註冊致使沒法初始化便會報錯
            依賴多個bean的時候,可使用逗號或者空格分開
    -->
    <bean id="person1" parent="person" depends-on="carSub"/>

 @Test
    public void testDepends() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
        Person person1 = (Person) context.getBean("person1");
        System.out.println(person1);
    }
複製代碼

9.bean的做用域

Spring中的bean的做用域有如下幾種: singleton, property, request,session 默認狀況下,spring中每一個bean只有一個實例,可是能夠顯示指定。

<!--
        bean的生命週期:
            singleton:默認選項,IOC容器默認爲每一個bean只實例化一個實例,該實例在調用具體的bean以前完成。
            property:原型。該方式下IOC容器會在每次調用bean的時候實例化一個新的實例,而且只有在調用時進行bean的初始化
           request和session同JavaWeb中同樣,在此很少加介紹
    -->

    <bean id="dog" class="com.spring.demo.bean.Dog" p:name="haki" scope="prototype"/>

package com.spring.demo.bean;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@AllArgsConstructor
@ToString
public class Dog {
    private String name;
    public Dog() {
        super();
        System.out.println("dog's constructor");
    }
}

@Test
    public void testScope() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
        Dog dog = (Dog) context.getBean("dog");
        Dog dog1 = (Dog) context.getBean("dog");
        System.out.println(dog == dog1);
    }
複製代碼

在上面的demo中,除了能夠查看bean的建立個數,還應該查看bean的建立時機。

相關文章
相關標籤/搜索