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