配置元數據描述了Spring容器在應用程序中是如何實例化、配置和組裝對象的。配置的方式有XML配置、註解配置、Java配置。
一、Bean XML配置流程
Spring的配置至少需要一個或多個由容器管理的bean.基於XML的配置元數據,需要用<beans>元素內的<bean>元素來配置。
1、基於XML的配置元數據的基本結構(定義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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="..." class="...">
<!--放置這個bean的協作者和配置-->
</bean>
<bean id="..." class="...">
<!--放置這個bean的協作者和配置-->
</bean>
</beans>
id屬性是用於標識單個bean定義的字符串,它的值指協作對象,class屬性定義bean的類型,並使用完全限定的類名。
應用舉例:
<bean id="customerBean" class="com.herry.demo.Customer">
<property name="name" value="herry"></property>
</bean>
bean的命名:在基於XML配置元數據中,使用id或name屬性來指導bean標識符。bean的命名遵循一個小寫字母開頭的駱駝命名規則。
(1)作用域配置
Spring 中創建bean時,可以指定作用域,作用域有以下5種類型
- 單例(singleton) 默認作用域,一個spring容器中只有Bean的一個實例。
-
原型(prototype)每次獲取Bean時生成一個新的實例。
-
請求(request)作用域是單個http請求,單個http請求只有Bean的一個實例。一旦請求完成,bean實例將被銷燬。
-
會話(session)作用域是單個會話,單個會話只有Bean的一個實例。一旦會話結束,bean實例將被銷燬。
-
全局會話(global-session)在Portlet應用程序中使用,每個全局會話只有Bean的一個實例。普通Servlet應用中與會話作用域無區別。
(2)設置初始化方法和銷燬方法
Bean在創建時,需要執行一些資源(數據庫、套接字、文件)申請等初始化工作,可以在Bean的初始化回調方法中處理,此方法由Spring容器調用。
同樣Bean在銷燬時,需要執行一些資源(數據庫、套接字、文件)申請等銷燬工作,可以在Bean的銷燬回調方法中處理,此方法由Spring容器調用。
應用舉例:
xml文件配置
<bean id="customerBean" class="com.herry.demo.Customer" init-method="init" destroy-method="close">
</bean>
創建初始化方法和銷燬方法
public class Customer {
//....
public void init() {
System.out.println("初始化...");
}
public void close() {
System.out.println("銷燬...");
}
}
運行效果:
2、實例化容器(創建Spring容器)
Spring IoC容器需要在應用啓動時進行實例化。在實例化過程中,IoC容器會從各種外部資源加載配置元數據,提供給ApplicationConext構造函數。
有兩種IoC容器:
- ApplicationContext
- BeanFactory
ApplicationContext容器是更高級更常用的容器,繼承並擴展了BeanFactory的功能。同樣ApplicationContext
本身是一個Java接口,常用的實現類是:
FileSystemXmlApplicationContext
: 通過文件路徑加載bean的xml配置文件ClassPathXmlApplicationContext
: 通過類路徑加載bean的xml配置文件WebXmlApplicationContext
: 通過web網址加載bean的xml配置文件
舉例:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Hello
{
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");// ApplicationContext容器加載application.xml
}
}
3、使用容器(通過spring容器獲取bean)
ApplicationContext提供的getBean()方法可用於檢索bean實例。
應用舉例:
//創建並配置bean
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
//檢索配置了的bean實例
Customer customerBean = (Customer) context.getBean("customerBean");
//使用bean實例
customerBean.displayInfo();
4、關閉容器
最後應用結束時需要關閉容器,釋放資源,容器中的所有bean也將被銷燬。
context.close();
應用舉例:
((ClassPathXmlApplicationContext) context).close();
二、Spring管理Bean
1、Bean的命名
Bean通過名稱進行區分,每個Bean至少有一個名稱,通過Bean名稱,可以引用其他Bean。命名方式有三種:
- id命名,只有一個,不能重複
- name命名
- 別名(aliase)
2、Bean的實例化方法
Bean的實例化方法有兩種:
- 構造函數實例化:分爲有參的構造函數和無參的構造函數
- 調用靜態或實例工廠方法
(1)構造函數實例化
Customer.java類如下所示,該類含有有參構造函數和無參構造函數
package com.herry.demo;
public class Customer {
String name;
//無參構造函數
public Customer() {
}
//有參構造函數
public Customer(String name) {
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void displayInfo() {
System.out.println("Hello: "+ name);
}
public void init() {
System.out.println("初始化...");
}
public void close() {
System.out.println("銷燬...");
}
}
XML配置調用無參構造函數實例化Bean
<bean id="customerBean" class="com.herry.demo.Customer" init-method="init" destroy-method="close">
</bean>
XML配置調用有參構造函數實例化Bean
<bean id="customerBean" class="com.herry.demo.Customer" init-method="init" destroy-method="close">
<constructor-arg value="herry"/>
</bean>
注意constructor-arg和property在參數傳遞的用法不同:
- constructor-arg:通過構造函數注入。
- property:通過setter對應的方法注入
(2) 調用靜態或實例工廠方法
添加Store.java文件
package com.herry.demo;
public class Store {
//靜態方法
public static Customer crateCustomer1()
{
Customer customer=new Customer();
customer.setName("herry");
return customer;
}
//工廠方法
public Customer crateCustomer2()
{
Customer customer=new Customer();
customer.setName("herry");
return customer;
}
}
調用靜態工廠方法實例化Bean,通過調用類中的factory-method特性所指定的靜態方法實現。
<bean id="customerBean" class="com.herry.demo.Store" factory-method="crateCustomer1">
</bean>
使用實例工廠方法實例化Bean,需要加入factory-bean特性,通過該特性引用包含實例工廠方法的工廠Bean.
<bean id="storeBean" class="com.herry.demo.Store" >
</bean>
<bean id="customerBean" factory-bean="storeBean" factory-method="crateCustomer2">
</bean>
3、Bean作用域
由Spring容器創建的Bean的生存期稱爲Bean作用域。作用域的設定使用<bean>元素的scope特性來指定一個Bean定義的作用域。
4、延遲初始化
默認的情況下,Spring容器在啓動階段創建Bean,優點是儘可能早的發現配置錯誤。Spring,XML配置中<bean>元素的lazy-init特性可以定Bean定義延遲,直到Bean需要使用時,才由容器創建。延遲初始化的優點是加快容器啓動時間,並且佔用較少的內存量。
5、生命週期回調
Bean定義回調方法,這些方法可以在Bean生命週期的任何特點時間點由容器調用。如基於XML配置的<bean>元素中的init-method和destory-method特性。
三、依賴注入
依賴注入的基本原則是應用程序對象不應該負責它們所依賴的資源或協作者,而是應該由IoC容器處理對象創建和依賴注入,從而導致資源查找的外部化。
關於依賴注入的優勢參考以下知乎回答:spring的依賴注入到底有什麼優勢? - 牛岱的回答 - 知乎 https://www.zhihu.com/question/27053548/answer/575335901
xml配置文件中,在bean的定義中可配置該bean的依賴項,通常使用的配置方式有2種:
- 基於構造函數注入
- 基於Setter方法注入
有以下代碼:
類A和組件B,A依賴於B,A的方法importantMethod用到了B。若B是接口,且有多個實現,則A的可重用性大大降低。依賴注入,即接管對象的創建工作,並將對象的引用注入到需要該對象的組件。依賴注入框架會分別創建對象A和對象B,並將對象B注入到對象A中。
1、基於構造函數注入
對於上述代碼,利用構造函數注入的形式如下:
構造函數注入在組件創建期間被執行。依賴項被表示爲構造函數的參數,容器通過檢查Bean定義中指定的構造函數參數來確定調用哪個構造函數。
使用<constructor-arg>的ref特性注入依賴項
應用舉例:
(1)編寫Customer類
package com.herry.demo;
public class Customer {
String name;
//有參構造函數
public Customer(String name) {
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage()
{
return "Hello: "+ name;
}
}
(2)編寫MessagePrinter類,該類依賴Customer類
package com.herry.demo;
import com.herry.demo.Customer;
public class MessagePrinter {
private Customer customer;
//定義有參構造函數
public MessagePrinter(Customer customer) {
this.customer=customer;
}
//使用注入的Customer的具體邏輯
public void displayInfo() {
System.out.println(this.customer.getMessage());
}
}
(3)編寫應用主類Hello
package com.herry.demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Hello
{
public static void main(String[] args) {
// @SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
MessagePrinter printer=context.getBean(MessagePrinter.class);
printer.displayInfo();
((ClassPathXmlApplicationContext) context).close();
}
}
(4)創建配置文件
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 定義bean -->
<bean id="customerBean" class="com.herry.demo.Customer" >
<constructor-arg name="name" value="herry"/>
</bean>
<!-- 依賴注入 -->
<bean id="messagePrinter" class="com.herry.demo.MessagePrinter">
<constructor-arg ref="customerBean"/>
</bean>
</beans>
測試結果:
2、基於Setter方法注入
對於舉例中的代碼使用Setter方法依賴注入形式如下:
A類中新增setB方法,該方法被spring框架調用,以注入B的一個實例。
Setter注入是在Bean實例化完成後執行。實例化後,通過調用bean的setter方法完成。
工程的目錄結構如下:
(1) Customer類與上面一樣不做任何修改
(2)MessagePrinter類修改爲如下:
package com.herry.demo;
import com.herry.demo.Customer;
public class MessagePrinter {
private Customer customer;
public MessagePrinter()
{
}
public Customer getCustomer() {
return customer;
}
//定義setter函數,setter方法注入bean
public void setCustomer(Customer customer) {
this.customer=customer;
}
//使用注入的Customer的具體邏輯
public void displayInfo() {
System.out.println(this.customer.getMessage());
}
}
(3) 主類也不要修改,做了如下調整,也可不變
package com.herry.demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Hello
{
public static void main(String[] args) {
// @SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
MessagePrinter printer=(MessagePrinter)context.getBean("messagePrinter");
printer.displayInfo();
((ClassPathXmlApplicationContext) context).close();
}
}
(4) 配置文件修改如下
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 定義bean -->
<bean id="customerBean" class="com.herry.demo.Customer" >
<constructor-arg name="name" value="herry"></constructor-arg>
</bean>
<!-- 依賴注入 -->
<bean id="messagePrinter" class="com.herry.demo.MessagePrinter">
<property name="customer" ref="customerBean"/>
</bean>
</beans>
配置文件中,Customer類通過構造函數方式實例化Bean;messagePrinter對象通過配置property元素來調用setter方法以設置值。
(5)運行結果如下: