1. 程式人生 > >Spring容器和bean的注入(1)

Spring容器和bean的注入(1)

第一節 在IOC容器中裝配Bean

1.1Spring容器成功啟動條件


1.匯入Spring框架相關的jar包

2.正確配置spring配置檔案

3.Bean的類都已放到應用程式的類路徑下

1.2Bean配置資訊的組成

Bean配置資訊是Bean的元資料資訊(類的資訊)


1.Bean的實現類

2.Bean的屬性資訊

3.Bean的依賴資訊

4.Bean的行為配置

1.3xml、bean、容器、應用程式之間的關係!

1.3

1.4基於xml的配置


    <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd"
>
<bean id="phone" class="com.test.testDom4j.Phone"> <property name="brand" value="小米"></property> <property name="type" value="6s"></property> <property name="price" value="2500"></property> </bean>
<bean id="car" class="com.test.testSpring.Car"> <constructor-arg value="1" index="0"></constructor-arg> <constructor-arg value="寶馬" index="1"></constructor-arg> <constructor-arg value="95.6" index="2"></constructor-arg> </bean> <bean id="car1" class="com.test.testSpring.Car"> <constructor-arg value="2" index="0"></constructor-arg> <constructor-arg value="奧迪" index="1"></constructor-arg> <constructor-arg value="65" index="2"></constructor-arg> </bean> <!-- 給list集合賦值 --> <bean id="person" class="com.test.testSpring.Person"> <property name="name"> <value>沈志龍</value> </property> <property name="age" > <value>26</value> </property> <property name="cars"> <list> <ref bean="car"/> <ref bean="car1"/> </list> </property> </bean> <!-- 給map集合賦值 --> <bean id="person1" class="com.test.testSpring.Person2"> <property name="name"> <value>沈志龍</value> </property> <property name="age" > <value>26</value> </property> <property name="cars"> <map> <entry key="baoma" value-ref="car"></entry> <entry key="aodi" value-ref="car1"></entry> </map> </property> </bean> <!-- 配置propertites --> <bean id="p1" class="com.test.testSpring.DataSource"> <property name="properties"> <props> <prop key="url">jdbc:mysql:///jb</prop> <prop key="driver">com.mysql.jdbc.Driver</prop> <prop key="username">root</prop> <prop key="username">root</prop> <prop key="password">as501226107A.</prop> </props> </property> </bean> <!-- 配置公共的list --> <util:list id="cars"> <ref bean="car"/> <ref bean="car1"/> </util:list> <bean id="person2" class="com.test.testSpring.Person"> <property name="name" value="劉德華"></property> <property name="age" value="57"></property> <property name="cars" ref="cars"></property> </bean> </beans>

第二節 Bean基本配置

2.1.1裝配一個bean


Spring配置檔案中定義一個簡要的bean配置片段

<bean id="Foo" class="com.dream.Foo"></bean>

2.1.2Bean的命名


在配置bean的時候,需要為bean指明一個id作為bean的名稱,id在IOC容器中是唯一的。

2.1.3使用 p 名稱空間

• 為了簡化 XML 檔案的配置,越來越多的 XML 檔案採用屬性而非子元素配置資訊。

• Spring 從 2.5 版本開始引入了一個新的 p 名稱空間,可以通過 元素屬性的方式配置 Bean 的屬性。

• 使用 p 名稱空間後,基於 XML 的配置方式將進一步簡化• 為了簡化 XML 檔案的配置,越來越多的 XML 檔案採用屬性而非子元素配置資訊。

• Spring 從 2.5 版本開始引入了一個新的 p 名稱空間,可以通過 元素屬性的方式配置 Bean 的屬性。

• 使用 p 名稱空間後,基於 XML 的配置方式將進一步簡化


<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">



    <bean name="john-classic" class="com.example.Person">

        <property name="name" value="John Doe"/>

        <property name="spouse" ref="jane"/>

    </bean>



    <bean name="john-modern" 

        class="com.example.Person"

        p:name="John Doe"

        p:spouse-ref="jane"/>



    <bean name="jane" class="com.example.Person">

        <property name="name" value="Jane Doe"/>

    </bean>

</beans>
上面的例子不僅使用p名稱空間包含了一個屬性(property)值,而且使用了一個特殊的格式聲明瞭一個屬性引用。在第一個bean定義中使用了`<property name="spouse" ref="jane"/>`來建立bean`john`到bean`jane`的引用,而第二個bean定義則採用`p:spouse-ref="jane"`屬性(attribute)的方式達到了同樣的目的。在這個例子中,"`spouse`"是屬性(property)名,而"`-ref`“則用來說明該屬性不是一個具體的值而是對另外一個bean的引用。

第三節 依賴注入

3.0理解IOC和DI

控制反轉:

依賴注入:

img

3.1屬性注入

3.1.1 Spring屬性注入例項


1.屬性注入是通過setXXX()方法注入Bean的屬性值或者依賴物件

2.屬性注入需要無參構造方法,容器需要通過反射來建立物件

<bean id="phone" class="com.test.testDom4j.Phone">

        <property name="brand" value="小米"></property>

        <property name="type" value="6s"></property>

        <property name="price" value="2500"></property>

</bean>

3.1.2 引數及元素名介紹

id: bean的唯一名稱,容器通過id來獲取bean

class:該bean類的全路徑,容器通過獲取路徑並使用反射的技術來建立例項

property: bean的屬性配置

name:屬性名稱,對應類的屬性

value:屬性的值(value也可以寫在property的內部)



<property name="name">

        <value>沈志龍</value>

</property>

3.1.6

常規做法


public class UserDao{

       public void addUser(String username) {

       System.out.println("新增使用者:"+username);  

    }  

}

public class UserMessage {

   String username="demo";

   UserDao userDao;

   public UserMessage(){

   userDao=new UserDao();  

}      

   public void addUser(){

  userDao.addUser(username);  

}  

}

public class test{

  UserMessage userMessage=new UserMessage();

  userMessage.addUser();

}
由於在UserMessage內部建立了UserDao物件,導致兩個類的耦合度較高,如果需要另一個Dao類來實現addUser方法,需要更改UserMessage的程式碼,重新new另一個類,如果系統中有很多地方用到這個Dao類,那麼我們需要修改很多次,出現Bug的機率很高,所以不推薦這種做法

面向介面




public interface UserDao{

  void addUser(String username);      

}



public class UserDaoImpl implements UserDao{

    @Override    

    public void addUser(String username)

    {

        System.out.println("新增使用者:"+username);  

    }  

}



public class UserMessage{    



    private UserDao userDao;    



    //使用設值方式賦值    

    public void setUserDao(UserDao userDao) {    

        this.userDao = userDao;    

    }    

    public void addUser(String userName, String password) {    



        userDao.addUser(userName);    

    }    

}   



public class test{

    public static void main(String[] args) {

        UserDao userDao =new UserDaoImpl();

        UserMessage userMessage=new UserMessage();

        userMessage.setUserDao(userDao);

    }

}


使用面向介面的思想降低了兩個類之間的耦合度,我們如果需要修改addUser的邏輯,我們只需要修改實現類中的程式碼,並不會去修改UserMessage的程式碼

Spring依賴注入

在面向介面的基礎上來,通過依賴注入來將所依賴的物件注入到Bean中


 userManager.xml



 <!-- 使用spring管理物件的建立,還有物件的依賴關係 -->    

<bean id="userManager" class="scau.zzf.service.UserMessage">    

        <!-- (1)UserMessageImpl使用了userDao,Ioc是自動建立相應的UserDao實現,都是由容器管理-->    

        <!-- (2)在UserMessageImpl中提供建構函式,讓spring將UserDao實現注入(DI)過來 -->    

        <!-- (3)讓spring管理我們物件的建立和依賴關係,必須將依賴關係配置到spring的核心配置檔案中 -->    

        <property name="userDao" ref="UserDao"></property>   

    <!-- 構造注入 -->

    <!-- <constructor-arg ref="userDao"/>  --> 

    </bean>   

<bean id="UserDao" class="scau.zzf.Dao.UserDao">



</bean>    



public interface UserDao{

  void addUser(String username);      

}



public class UserDaoImpl implements UserDao{

    @Override    

    public void addUser(String username)

    {

        System.out.println("新增使用者:"+username);  

    }  

}



public class UserMessage{    

    @Autowired

    private UserDao userDao;    



    //使用設值方式賦值    

    public void setUserDao(UserDao userDao) {    

        this.userDao = userDao;    

    }    

    public void addUser(String userName, String password) {    



        userDao.addUser(userName);    

    }    

}   



public class test{

    public static void main(String[] args) {

       ApplicationContext atc=new ClassPathXmlApplicationContext("userManager.xml");

        UserMessage userMessage=(UserMessage) atc.getBean("userMessage");

        userMessage.addUser("xx","xx");

    }

}


在建立容器的時候,容器會掃描xml中的bean配置資訊,並例項化相應的Bean新增到Spring容器中,在使用者需要使用Bean的時候,就可以通過傳入ApplicationContext的例項物件的getBean方法,傳入bean的id獲取相應的例項。

3.2 建構函式注入

3.2.1 按型別匹配注入


public class Car {

    private int id;

    private String type;

    private double price;

    private String place;

    /**

     * @param id

     * @param type

     * @param price

     */

    public Car(int id, String type, double price) {

        this.id = id;

        this.type = type;

        this.price = price;

    }

    /**

     * 

     */

    public Car() {

        super();

        // TODO Auto-generated constructor stub

    }



    public int getId() {

        return id;

    }

    public void setId(int id) {

        this.id = id;

    }

    public String getType() {

        return type;

    }

    public void setType(String type) {

        this.type = type;

    }

    public double getPrice() {

        return price;

    }

    public void setPrice(double price) {

        this.price = price;

    }

    public String getPlace() {

        return place;

    }

    public void setPlace(String place) {

        this.place = place;

    }

    @Override

    public String toString() {

        return "Car [id=" + id + ", type=" + type + ", price=" + price + ", place=" + place + "]";

    }

}



<bean id="car" class="com.test.testSpring.Car">

        <constructor-arg value="1" type="int"></constructor-arg>

        <constructor-arg value="寶馬" type="java.lang.String"></constructor-arg>

        <constructor-arg value="95.6"></constructor-arg>

</bean>
注意

1.如果不設定引數型別,則按照預設的constructor-arg配置順序去匹配,如果此時引數型別不對則會產生異常

1536976822845


警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'car2' defined in class path resource [applicationContext.xml]: Unsatisfied dependency expressed through constructor parameter 0: Could not convert argument value of type [java.lang.String] to required type [int]: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "奧迪"

2.可以代表構造方法的入參順序嗎?

如果只有一個相同入參型別的構造方法,可以代表;但是如果存在多個具有相同型別入參的構造方法,它的順序標識就失效了

3.的順序不會對最終的配置結果產生影響,但是如果存在多個相同入參型別的構造方法,則需要使用到索引匹配和型別匹配的配合

1536976723849

1536976591460

3.2.2按照索引匹配注入


public Car( String place,String type,double price) {

        this.type = type;

        this.price = price;

        this.place = place;

    }
此時如果使用型別注入,分不清第一個和第二個是到底是注入的哪個屬性,產生了不確定性,此時就需要使       用索引匹配注入。

1536977390949

1536977423283

注意
1.index屬性的下標從0開始,0代表第一個引數



2.通常聯合使用型別和索引匹配入參

3.3 工廠方法注入

第四節 注入引數詳解

4.1.1 引用其它 Bean

• 組成應用程式的 Bean 經常需要相互協作以完成應用程式的功能. 要使 Bean 能夠相互訪問, 就必須在 Bean 配置檔案中指定對 Bean 的引用

• 在 Bean 的配置檔案中, 可以通過 元素或 ref 屬性為 Bean 的屬性或構造器引數指定對 Bean 的引用.

• 也可以在屬性或構造器裡包含 Bean 的宣告, 這樣的 Bean 稱為內部 Bean

1536977746318

4.1.2 內部bean

• 當 Bean 例項僅僅給一個特定的屬性使用時, 可以將其宣告為內部 Bean. 內部 Bean 宣告直接包含在 元素裡, 不需要設定任何 id 或 name 屬性

內部 Bean 不能使用在任何其他地方

4.1.3引入集合

Collection集合

• 在 Spring中可以通過一組內建的 xml 標籤(例如: , 或 ) 來配置集合屬性.

• 配置 java.util.List 型別的屬性, 需要指定 標籤, 在標籤裡包含一些元素. 這些標籤可以通過 指定簡單的常量值, 通過 指定對其他 Bean 的引用. 通過 指定內建 Bean 定義. 通過 指定空元素. 甚至可以內嵌其他集合.

• 陣列的定義和 List 一樣, 都使用

• 配置 java.util.Set 需要使用 標籤, 定義元素的方法與 List 一樣.

1536978299564

Map

• Java.util.Map 通過 標籤定義, 標籤裡可以使用多個 作為子標籤. 每個條目包含一個鍵和一個值.

• 必須在 標籤裡定義鍵

• 因為鍵和值的型別沒有限制, 所以可以自由地為它們指定 , , 元素.

• 可以將 Map 的鍵和值作為 的屬性定義: 簡單常量使用 key 和 value 來定義; Bean 引用通過 key-ref 和 value-ref 屬性定義

• 使用 定義 java.util.Properties, 該標籤使用多個 作為子標籤. 每個 標籤必須定義 key 屬性.

1536978307014

4.1.4自動裝配

• Spring IOC 容器可以自動裝配 Bean. 需要做的僅僅是 的 autowire 屬性裡指定自動裝配的模式

byType(根據型別自動裝配): 若 IOC 容器中有多個與目標 Bean 型別一致的 Bean. 在這種情況下, Spring 將無法判定哪個 Bean 最合適該屬性, 所以不能執行自動裝配.

byName(根據名稱自動裝配): 必須將目標 Bean 的名稱和屬性名設定的完全相同.

• constructor(通過構造器自動裝配): 當 Bean 中存在多個構造器時, 此種自動裝配方式將會很複雜. 不推薦使用

第五節 Bean 的作用域

• 在 Spring 中, 可以在 元素的 scope 屬性裡設定 Bean 的作用域.

預設情況下, Spring 只為每個在 IOC 容器裡宣告的 Bean 建立唯一一個例項, 整個 IOC 容器範圍內都能共享該例項:所有後續的 getBean() 呼叫和 Bean 引用都將返回這個唯一的 Bean 例項.該作用域被稱為 singleton, 它是所有 Bean 的預設作用域.

1536978368212

第六節 使用外部屬性檔案

• 在配置檔案裡配置 Bean 時, 有時需要在 Bean 的配置裡混入==系統部署的細節資訊==(例如: 檔案路徑, 資料來源配置資訊等). 而這些部署細節實際上需要和 Bean 配置相分離

• Spring 提供了一個 PropertyPlaceholderConfigurer 的 BeanFactory 後置處理器, 這個處理器允許使用者將 Bean 配置的部分內容外移到屬性檔案中. 可以在 Bean 配置檔案裡使用形式為 ${var} 的變數, PropertyPlaceholderConfigurer 從屬性檔案里加載屬性, 並使用這些屬性來替換變數.

• Spring 還允許在屬性檔案中使用 ${propName},以實現屬性之間的相互引用。

====

1536978415844

第七節 整合多個配置檔案

• Spring 允許通過 將多個配置檔案引入到一個檔案中,進行配置檔案的整合。這樣在啟動 Spring 容器時,僅需要指定這個合併好的配置檔案就可以。

• import 元素的 resource 屬性支援 Spring 的標準的路徑資源

7.1-1

7.1-2

第八節 基於註解的配置

8.1.1使用註解定義Bean

使用@Compnent註解,該類可以被Spring容器識別,Spring容器將自動將標註的類轉化為容器中的Bean


@Compnent("userDao")



public class UserDao{



    ....



}

等同於

<bean id="userDao" class="xx.xx.UserDao"/>

與@Compnent功能基本相同的三個註解:

@Repository :用於對DAO實現類進行標註



@Service:用於對Service實現類進行標註



@Controller:用於對Controller實現類進行標註

提供這三個註解是為了讓標註類本身的用途清晰化

8.1.2掃描註解定義的Bean

Sping提供了一個context名稱空間,它提供了通過掃描類包以應用Bean的方式


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context="http://www.springframework.org/schema/context"

    xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

        <context:component-scan base-package="com.test.annoation"></context:component-scan>

</beans>






Controller


/**

 * 

 */

package com.test.annoation;



import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import org.springframework.stereotype.Controller;



/**

  *  Class Name: BookController.java

  *  Description: 

  *  @author  dragon  DateTime 2018年9月13日 下午8:14:20 

  *  @company bvit 

  *  @email  [email protected] 

  *  @version 1.0

  */

@Controller

public class BookController {

    @Autowired

    private BookService bookService;

    public void bookAdd() {

        System.out.println("BookController.bookAdd");

        bookService.add();

    }

}


Serivice


@Service

public class BookService {

    @Autowired

    private BookDao bookDao;

    public void add() {

        System.out.println("bookService .add");

        bookDao.add();

    }

}

Dao


public interface BookDao {

    public void add();

}

@Repository

public class BookDaoImpl implements BookDao {



    /* (non-Javadoc)

     * @see com.test.annoation.BookDao#add()

     */

    public void add() {

        // TODO Auto-generated method stub

        System.out.println("add");

    }



}
對於掃描到的元件, **Spring** **有預設的命名策略**: 使用非限定類名, 第一個字母小寫**.** **也可以在註解中通過 value** **屬性值標識元件的名稱**

• 當在元件類上使用了特定的註解之後, 還需要在 Spring 的配置檔案中宣告

base-package 屬性指定一個需要掃描的基類包Spring 容器將會掃描這個基類包裡及其子包中的所有類.

當需要掃描多個包時, 可以使用逗號分隔.

對類的過濾

• 如果僅希望掃描特定的類而非基包下的所有類,可使用 resource-pattern 屬性過濾特定的類,示例:

子節點表示要包含的目標類

子節點表示要排除在外的目標類

• 下可以擁有若干個 和 子節點

過濾表示式

1536981898606

注意

注意

注意

中擁有一個use-default-filters屬性,其預設值為true,標識會預設掃描上述四個註解標註的Bean。如果想僅掃描@Controller中的bean,需要將該屬性設定為false

8.1.3 自動裝配Bean

• 元素還會自動註冊 AutowiredAnnotationBeanPostProcessor 例項, 該例項可以自動裝配具有 @Autowired 和 @Resource 、@Inject註解的屬性.

使用 @Autowired 自動裝配 Bean

1536981250631

   @Autowired 註解自動裝配**具有相容型別**的單個 Bean屬性

構造器, 普通欄位(*即使是非 public),* 一切具有引數的方法都可以應用@Authwired 註解

@Autowired會將方法中的引數Bean自動注入到方法中



![1536982224565](/8.1.3-2.png)

預設情況下, 所有使用 @Authwired 註解的屬性都需要被設定. 當 Spring 找不到匹配的 Bean 裝配屬性時, 會丟擲異常, 若某一屬性允許不被設定, 可以設定 @Authwired 註解的 required 屬性為 false

– 預設情況下, 當 IOC 容器裡存在多個型別相容的 Bean 時, 通過型別的自動裝配將無法工作. 此時可以在 @Qualifier 註解裡提供 Bean 的名稱. Spring 允許對方法的入參標註 @Qualifiter 已指定注入 Bean 的名稱

– @Authwired 註解也可以應用在陣列型別的屬性上, 此時 Spring 將會把所有匹配的 Bean 進行自動裝配.

– @Authwired 註解也可以應用在集合屬性上, 此時 Spring 讀取該集合的型別資訊, 然後自動裝配所有與之相容的 Bean.

– @Authwired 註解用 java.util.Map 上時, 若該 Map 的鍵值為 String, 那麼 Spring 將自動裝配與之 Map 值型別相容的 Bean, 此時 Bean 的名稱作為鍵值

使用 @Resource 或 @Inject 自動裝配 Bean

• Spring 還支援 @Resource 和 @Inject 註解,這兩個註解和 @Autowired 註解的功用類似

• @Resource 註解要求提供一個 Bean 名稱的屬性,若該屬性為空,則自動採用標註處的變數或方法名作為 Bean 的名稱

• @Inject 和 @Autowired 註解一樣也是按型別匹配注入的 Bean, 但沒有 reqired 屬性

建議使用 @Autowired 註解

8.1.4 Bean生命過程

Spring IOC 容器可以管理 Bean 的生命週期, Spring 允許在 Bean 生命週期的特定點執行定製的任務.

• Spring IOC 容器對 Bean 的生命週期進行管理的過程:

–      通過構造器或工廠方法建立 Bean 例項



–      為 Bean 的屬性設定值和對其他 Bean 的引用



–      **呼叫 Bean** **的初始化方法**



–      Bean 可以使用了



–      **當容器關閉時,** **呼叫 Bean** **的銷燬方法**

• 在 Bean 的聲明裡設定 init-method 和 destroy-method 屬性, 為 Bean 指定初始化和銷燬方法.