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.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
控制反轉:
依賴注入:
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配置順序去匹配,如果此時引數型別不對則會產生異常
警告: 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.的順序不會對最終的配置結果產生影響,但是如果存在多個相同入參型別的構造方法,則需要使用到索引匹配和型別匹配的配合
3.2.2按照索引匹配注入
public Car( String place,String type,double price) {
this.type = type;
this.price = price;
this.place = place;
}
此時如果使用型別注入,分不清第一個和第二個是到底是注入的哪個屬性,產生了不確定性,此時就需要使 用索引匹配注入。
注意
1.index屬性的下標從0開始,0代表第一個引數
2.通常聯合使用型別和索引匹配入參
3.3 工廠方法注入
第四節 注入引數詳解
4.1.1 引用其它 Bean
• 組成應用程式的 Bean 經常需要相互協作以完成應用程式的功能. 要使 Bean 能夠相互訪問, 就必須在 Bean 配置檔案中指定對 Bean 的引用
• 在 Bean 的配置檔案中, 可以通過 元素或 ref 屬性為 Bean 的屬性或構造器引數指定對 Bean 的引用.
• 也可以在屬性或構造器裡包含 Bean 的宣告, 這樣的 Bean 稱為內部 Bean
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 一樣.
Map
• Java.util.Map 通過
• 必須在 標籤裡定義鍵
• 因為鍵和值的型別沒有限制, 所以可以自由地為它們指定 , , 或 元素.
• 可以將 Map 的鍵和值作為 的屬性定義: 簡單常量使用 key 和 value 來定義; Bean 引用通過 key-ref 和 value-ref 屬性定義
• 使用 定義 java.util.Properties, 該標籤使用多個 作為子標籤. 每個 標籤必須定義 key 屬性.
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 的預設作用域.
第六節 使用外部屬性檔案
• 在配置檔案裡配置 Bean 時, 有時需要在 Bean 的配置裡混入==系統部署的細節資訊==(例如: 檔案路徑, 資料來源配置資訊等). 而這些部署細節實際上需要和 Bean 配置相分離
• Spring 提供了一個 PropertyPlaceholderConfigurer 的 BeanFactory 後置處理器, 這個處理器允許使用者將 Bean 配置的部分內容外移到屬性檔案中. 可以在 Bean 配置檔案裡使用形式為 ${var} 的變數, PropertyPlaceholderConfigurer 從屬性檔案里加載屬性, 並使用這些屬性來替換變數.
• Spring 還允許在屬性檔案中使用 ${propName},以實現屬性之間的相互引用。
====
第七節 整合多個配置檔案
• Spring 允許通過 將多個配置檔案引入到一個檔案中,進行配置檔案的整合。這樣在啟動 Spring 容器時,僅需要指定這個合併好的配置檔案就可以。
• import 元素的 resource 屬性支援 Spring 的標準的路徑資源
第八節 基於註解的配置
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 屬性過濾特定的類,示例:
• 子節點表示要包含的目標類
• 子節點表示要排除在外的目標類
• 下可以擁有若干個 和 子節點
過濾表示式
注意
注意
注意
中擁有一個use-default-filters屬性,其預設值為true,標識會預設掃描上述四個註解標註的Bean。如果想僅掃描@Controller中的bean,需要將該屬性設定為false
8.1.3 自動裝配Bean
• 元素還會自動註冊 AutowiredAnnotationBeanPostProcessor 例項, 該例項可以自動裝配具有 @Autowired 和 @Resource 、@Inject註解的屬性.
使用 @Autowired 自動裝配 Bean
@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 指定初始化和銷燬方法.