IOC 和 DI
- IOC(Inversion of Control):其思想是反轉資源獲取的方向. 傳統的資源查找方式要求組件向容器發起請求查找資源。
作爲迴應, 容器適時的返回資源. 而應用了 IOC 之後, 則是容器主動地將資源推送給它所管理的組件,組件所要做的僅是選擇一種合適的方式來接受資源. 這種行爲也被稱爲查找的被動形式。 - DI(Dependency Injection) — IOC 的另一種表述方式:即組件以一些預先定義好的方式(例如: setter方法)接受來自如容器的資源注入. 相對於 IOC 而言,這種表述更直接。
介紹IOC和DI這兩個概念是用來可以更好的去理解spring如何去創建對象的,在上一篇文章中使用spring獲取到了ApplicationContext對象後,可以直接用這個對象去實例化HelloWorld對象,而不需要去使用new來創建一個對象。那麼spring是如何去創建對象的,前面提到過ApplicationContext是Spring的IOC容器,在這個容器中spring爲我們去管理在配置文件中我們配置的哪些類的對象,需要使用時向spring中獲取這個對象,對象使用完畢後spring自動去回收這個對象。
在 Spring 的 IOC 容器裏配置 Bean
在 xml 文件中通過 bean 節點來配置 bean
<!--通過全類名的方式類配置bean-->
<bean id="helloworld" class="com.iflytek.test.bean.HelloWorld">
</bean>
id:Bean 的名稱。
- 在 IOC 容器中必須是唯一的 。
- 若 id 沒有指定,Spring 自動將權限定性類名作爲 Bean 的名字。
- id可以指定多個名字,名字之間可用逗號、分號、或空格分隔。
Spring 容器
在 Spring IOC 容器讀取 Bean 配置創建 Bean 實例之前, 必須對它進行實例化. 只有在容器實例化後, 纔可以從 IOC 容器裏獲取 Bean 實例並使用.。
Spring 提供了兩種類型的 IOC 容器實現.
- BeanFactory: IOC 容器的基本實現.。
- ApplicationContext: 提供了更多的高級特性. 是BeanFactory 的子接口.。
其中BeanFactory 是 Spring 框架的基礎設施,面向 Spring本身; ApplicationContext 面向使用 Spring 框架的開發者,幾乎所有的應用場合都直接使ApplicationContext 而非底層的 BeanFactory 無論使用何種方式, 配置文件時相同的。
ApplicationContext
1.ApplicationContext的繼承結構:
2.ApplicationContext 的主要實現類:
- ClassPathXmlApplicationContext:從類路徑下加載配置文件。
- FileSystemXmlApplicationContext: 從文件系統中加載配置文件。
3.ConfigurableApplicationContext 擴展於 ApplicationContext,新增加兩個主要方法:refresh() 和 close(), 讓 ApplicationContext 具有啓動、刷新和關閉上下文的能力。
4.ApplicationContext 在初始化上下文時就實例化所有單例的 Bean。
從 IOC 容器中獲取 Bean
調用 ApplicationContext 的 getBean(String) 方法,其中傳入的參數是在配置文件中配置bean時聲明的id。也可以調用getBean(Class) 方法,在使用該方法獲取bean的時候需要注意,如過在配置文件中聲明的同一個類有配置2個bean,那麼spring是無法知道你是要獲取哪一個bean的(當然一般情況下也不會出現這種配置兩次的情況)。
依賴注入的方式
Spring 支持 3 種依賴注入的方式
- 屬性注入
- 構造器注入
- 工廠方法注入(很少使用,不推薦)
屬性注入
-屬性注入即通過 setter 方法注入Bean 的屬性值或依賴的對象
-屬性注入使用 元素, 使用 name 屬性指定 Bean 的屬性名稱,value 屬性或 子節點指定屬性值
-屬性注入是實際應用中最常用的注入方式
<!--通過全類名的方式類配置bean-->
<bean id="helloworld" class="com.iflytek.test.bean.HelloWorld">
<!-- 爲屬性賦值 -->
<!-- 通過屬性注入: 通過 setter 方法注入屬性值 -->
<property name="name" value="tom"></property>
</bean>
這種注入方式是之前的例子中寫過的
構造方法注入
通過構造方法注入Bean 的屬性值或依賴的對象,它保證了 Bean 實例在實例化後就可以使用。
構造器注入在 元素裏聲明屬性, 中沒有 name 屬性
將屬性注入註釋掉,使用構造器注入來看效果
<!-- 通過構造器注入屬性值 -->
<bean id="helloworld" class="com.iflytek.test.bean.HelloWorld">
<!--<property name="name" value="tom"></property> -->
<!-- 要求: 在 Bean 中必須有對應的構造器. -->
<constructor-arg value="lily"></constructor-arg>
</bean>
對用的HelloWorld實體類
public class HelloWorld {
private String name;
//在無參的構造函數中打印一條消息
public HelloWorld() {
super();
System.out.println("Constructed is used...");
}
public HelloWorld(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
// //在set方法中打印信息
// public void setName(String name) {
// System.out.println("after name:"+name);
// this.name = name;
// System.out.println("before name:"+name);
// }
//
public void hello(){
System.out.println("hello:"+name);
}
}
控制檯打印的結果:
使用構造器注入屬性值的時候需要注意
寫一個新的實體類
public class Car {
private int maxSpeed;
private String name;
private float price;
public Car(int maxSpeed, String name, float price) {
super();
this.maxSpeed = maxSpeed;
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Car :maxSpeed=" + maxSpeed + ", name=" + name + ", price=" + price ;
}
}
<!-- 若一個 bean 有多個構造器, 如何通過構造器來爲 bean 的屬性賦值 -->
<!-- 可以根據 index 和 value 進行更加精確的定位. (瞭解) -->
<bean id="car" class="com.iflytek.test.bean.Car">
<constructor-arg>
<value>10</value>
</constructor-arg>
<constructor-arg value="寶馬"></constructor-arg>
<constructor-arg value="15.5"></constructor-arg>
</bean>
<bean id="car1" class="com.iflytek.test.bean.Car">
<constructor-arg value="15.5" type="float"></constructor-arg>
<constructor-arg index="0">
<value>10</value>
</constructor-arg>
<!-- 若字面值中包含特殊字符, 則可以使用 DCDATA 來進行賦值. (瞭解) -->
<constructor-arg index="1">
<value><![CDATA[寶馬@__@]]></value>
</constructor-arg>
</bean>
控制檯打印的結果:
###字面值
- 字面值:可用字符串表示的值,可以通過 元素標籤或 value 屬性進行注入。
- 基本數據類型及其封裝類、String等類型都可以採取字面值注入的方式
- 若字面值中包含特殊字符,可以使用