Spring IoC (控制反轉(zhuǎn))之 xml 配置
1. 前言
本小節(jié)目的在于帶領(lǐng)大家熟練 xml 文件配置, 應(yīng)用 xml 文件配置 IoC。
在第二節(jié)中我們通過一個(gè)入門工程簡單地體驗(yàn)了一把 Spring 的使用。在第三節(jié)中梳理了一下 Spring 的工作流程。
大家有了一個(gè)初步認(rèn)知,Spring 框架的工作脫離不了核心配置文件 applicationContext.xml
。
在配置文件中我們目前只用到了一個(gè) bean 標(biāo)簽,它的作用是用于描述 Java 的類,讓框架啟動(dòng)加載配置文件實(shí)例化的。
疑問導(dǎo)出
那么我們知道描述一個(gè)類有幾個(gè)要素,類名、屬性、構(gòu)造函數(shù) set 和 get 方法對吧?而 bean 標(biāo)簽如何描述一個(gè)詳細(xì)的類呢?
帶著疑問… 開始本節(jié)內(nèi)容。
2.bean 標(biāo)簽中的屬性介紹
核心配置文件回顧
<bean id="user" class="com.wyan.entity.User" ></bean>
在上面的代碼中可以看到,在 bean 標(biāo)簽中有兩個(gè)屬性,一個(gè)是 id 一個(gè)是 class。那么在 bean 標(biāo)簽中都有哪些屬性呢?
屬性列表
學(xué)號(hào) | 姓名 |
---|---|
id | 定義的唯一標(biāo)識(shí) |
name | 同 id 的意義一致 |
class | 類 |
factory-bean | 工廠對象 |
factory-method | 工廠方法 |
init-method | 初始化執(zhí)行的方法 |
destroy-method | 銷毀執(zhí)行的方法 |
scope | 對象的作用域 |
lazy-init | 懶加載 |
autowire | 依賴注入 |
depends-on | 依賴于某個(gè)實(shí)例 |
疑問導(dǎo)出
上述屬性是配置 bean 標(biāo)簽中可以選擇的屬性,當(dāng)然一般來講,我們無需配置所有,可以根據(jù)自己的需求配置需要的屬性信息,那么如何選擇這些屬性呢?
2.1 屬性詳細(xì)解釋
2.1.1 id 和 name 標(biāo)簽的使用
我們目前已經(jīng)知道所有被實(shí)例化后的對象都存在于 Spirng 的容器中,那么從容器中獲取這些對象需要一個(gè)屬性 id 對吧?那么 name 和 id 有什么關(guān)系呢?
查看官方文檔得知 Spring 的容器會(huì)給初始化的每個(gè) bean 都定義一個(gè)或多個(gè)標(biāo)識(shí)符。這些標(biāo)識(shí)符在容器內(nèi)必須是唯一的。一個(gè) bean 通常只有一個(gè)標(biāo)識(shí)符。而 name 和 id 都可以起到標(biāo)識(shí)符的作用。
所以在 XML 配置文件,我們一般使用 id 或者 name 屬性,定義 bean 的唯一標(biāo)識(shí),
這樣我們才能通過定義好的唯一標(biāo)識(shí),從 Spring 的容器中獲取他們。
代碼實(shí)例:
xml 的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" name="user2" class="com.wyan.entity.User" ></bean>
</beans>
測試代碼如下:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println(context.getBean("user"));
System.out.println(context.getBean("user2"));
}
結(jié)果如圖所示:
結(jié)論證明:
我們通過 bean 標(biāo)簽中的 id 屬性 user, 或者使用 bean 標(biāo)簽中的 name 屬性 user2, 都可以得到 Spring 容器中的 user 對象的示例,而且打印的地址是同一個(gè)。我們之前說過一句,默認(rèn)在容器中的實(shí)例都是單例的,在這里也得到了證明。
2.1.2 class 屬性
bean 標(biāo)簽的定義實(shí)質(zhì)上是創(chuàng)建一個(gè)或多個(gè)對象的方法。當(dāng) xml 文件被解析加載的時(shí)候,使用該 bean 定義封裝的配置數(shù)據(jù)來創(chuàng)建(或獲取)實(shí)際對象,而創(chuàng)建獲取的對象是誰呢?就是通過 class 屬性中定義的類的全路徑來指定 。
一般來講 class 中的類實(shí)例化有兩種方式:
? 一種是反射 ,相當(dāng)于我們使用的 new 關(guān)鍵字。這種也是我們常用的方式。當(dāng)然不要忘記提供無參數(shù)的構(gòu)造方法(類中默認(rèn)有無參構(gòu)造,但是如果自定義了有參構(gòu)造,默認(rèn)的無參不會(huì)提供)
? 一種是工廠模式 ,需要借助于 factory-bean 和 factory-method 兩個(gè)屬性,這種方式不常用,我們可以了解下。
2.1.3 factorybean 和 factorymethod 屬性
這兩個(gè)屬性主要用于工廠模式實(shí)例化 bean 的時(shí)候使用,不是很常見。工廠模式有兩種,這里分別做個(gè)實(shí)例,幫助大家理解。
靜態(tài)工廠模式實(shí)例:
<!--applicationContext的配置bean節(jié)點(diǎn)-->
<bean id="user" class="com.wyan.entity.User" factory-method="createUserInstance"/>
創(chuàng)建 bean 示例的 Java 工廠類:
public class User {
private static User user = new User();
private User() {}
public static User createInstance() {
return user;
}
}
解釋:在定義使用靜態(tài)工廠方法創(chuàng)建的 bean 時(shí),class 屬性指定的是被創(chuàng)建的類,包含靜態(tài)的方法,并使用 factory-method 屬性來指定工廠方法本身名稱。
普通工廠模式:
<!--spring實(shí)例化工廠對象 用于創(chuàng)建java實(shí)例 -->
<bean id="beanFactory" class="com.wyan.factory.BeanFactory"></bean>
<!-- 被工廠創(chuàng)建的對象實(shí)例 -->
<bean id="user1" factory-bean="beanFactory" factory-method="createUser1"/>
工廠類代碼:
public class BeanFactory {
private static User1 user1 = new User1();
private static User2 user2 = new User2();
public User1 createUser1() {
return user1;
}
public User2 createUser2() {
return user2;
}
}
解釋:先實(shí)例化先創(chuàng)建各個(gè)對象示例的工廠對象到容器中,自身的 bean 標(biāo)簽將 class
屬性保留為空,并在 factory-bean
屬性中指定當(dāng)前容器中的工廠 Bean 名稱,再使用 factory-method
屬性設(shè)置創(chuàng)建示例的方法名稱。
2.1.4 init-method 和 destroy-method 屬性的使用
這兩個(gè)屬性比較好理解 init-method 就是 bean 被初始化后執(zhí)行的方法,destory-method 就是 bean 被銷毀執(zhí)行的代碼。
我們來個(gè)測試類:
public class User {
public User(){
System.out.println("我被spring實(shí)例化了");
}
public void initMethod(){
System.out.println("user類實(shí)例化時(shí)候執(zhí)行的代碼");
}
public void destoryMethod(){
System.out.println("user類實(shí)例被銷毀時(shí)候執(zhí)行的代碼");
}
}
配置文件:
<bean id="user" name="user2" class="com.wyan.entity.User" init-method="initMethod" destroy-method="destoryMethod" ></bean>**
測試代碼:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
加載 Spring 的配置文件控制臺(tái)打印如下:
有個(gè)小疑問:銷毀語句沒打印呢?那是因?yàn)椴]有調(diào)用容器的銷毀方法。
改造測試代碼如下:
public static void main(String[] args) {
AbstractApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
context.close();
}
解釋:ApplicationContext 沒有 close 方法使用它的子類
運(yùn)行結(jié)果:
2.1.5 其余屬性作用
scope :指定示例的作用范圍,后續(xù)章節(jié)詳細(xì)講解;
lazy-init :表示是否為懶加載;
autowire :指定屬性注入方式,后續(xù)章節(jié)詳解;
depends-on: 表示是否有依賴的 bean 對象,后續(xù)依賴注入章節(jié)詳細(xì)解釋。
2.2 構(gòu)造函數(shù)的使用
剛剛我們詳細(xì)解釋了 bean 標(biāo)簽內(nèi)部的屬性,經(jīng)過幾個(gè)小實(shí)例以后不禁也有個(gè)問題:
如果我們定義的類中有一些初始化的參數(shù),并且定義好了有參數(shù)的構(gòu)造,通過 xml 配置文件如何體現(xiàn)呢?
實(shí)現(xiàn)起來非常簡單,跟我來進(jìn)行一個(gè)小實(shí)例:
改造 User 類:
這是一個(gè)普通的 Java 類對象,包含兩個(gè)屬性及其 get 和 set 方法,并且提供了空參構(gòu)造和有參構(gòu)造,為了測試方便再覆寫一個(gè) toString 方法。
public class User {
private Integer id;
private String name;
public User() {
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
xml 配置文件方式:
<bean id="user" class="com.wyan.entity.User" >
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="zs"></constructor-arg>
</bean>
測試結(jié)果:
其實(shí)對于有參構(gòu)造實(shí)例化對象而言,使用一個(gè)標(biāo)簽 constructor-arg 即可,表示構(gòu)造的參數(shù),如果有多個(gè),可以繼續(xù)添加,這里不多做演示。
疑問導(dǎo)出:
可能有同學(xué)會(huì)想,那么如果以后我們的屬性需要?jiǎng)討B(tài)更改呢?或者我們的屬性不是基本類型而是另外的對象呢? 后續(xù)在第三章依賴注入多種屬性的小節(jié)給大家講解 。
3. 小結(jié)
本章節(jié)帶著大家詳細(xì)解釋了 bean 標(biāo)簽的使用,那么通過本章節(jié)我們收獲了哪些呢?
-
容器內(nèi)部命名唯一標(biāo)識(shí)可以通過 id 也可以通過 name;
-
實(shí)例化對象有兩種方式 反射模式和工廠模式;
-
如果是反射模式,那么必須配置 class 屬性,因?yàn)樾枰?class 屬性中類的全路徑來實(shí)例化 bean 對象;
-
如果需要在類實(shí)例化初始化參數(shù),可以使用 init 方法也可以使用有參構(gòu)造。
持之以恒的學(xué)習(xí)是成功的最快捷徑… 切記眼高手低。