Spring IOC容器可以管理Bean的生命周期,允許在Bean生命周期的特定點執行定制的任務。
Spring IOC容器對Bean的生命周期進行管理的過程如下:
(1).通過構造器或工廠方法創建Bean實例。
(2).為Bean的屬性設置值和對其它Bean的引用。
(3).調用Bean的初始化方法。
(4).Bean的使用。
當容器關閉時,調用Bean的銷毀方法。
在 Bean 的聲明裡設置 init-method 和 destroy-method 屬性, 為 Bean 指定初始化和銷毀方法。
示例:User實體類:
package com.atguigu.spring.helloworld; import java.util.List; public class User { private String userName; private List<Car> cars; private String wifeName; public String getWifeName() { return wifeName; } public void setWifeName(String wifeName) { System.out.println("設置wifeName屬性。。。"+wifeName); this.wifeName = wifeName; } public String getUserName() { return userName; } public void setUserName(String userName) { System.out.println("設置userName屬性。。。"+userName); this.userName = userName; } public List<Car> getCars() { return cars; } public void setCars(List<Car> cars) { this.cars = cars; } // 構造方法 public User() { System.out.println("正在使用構造方法 Construtor..."); } @Override public String toString() { return "User [userName=" + userName + ", cars=" + cars + "]"; } // 初始化方法 public void init(){ System.out.println("init method..."); } // 銷毀方法 public void destroy(){ System.out.println("destroy method..."); } }
配置文件beans-auto.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" 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.0.xsd"> <!-- 自動裝配: 只聲明 bean, 而把 bean 之間的關系交給 IOC 容器來完成 --> <!-- byType: 根據類型進行自動裝配. 但要求 IOC 容器中只有一個類型對應的 bean, 若有多個則無法完成自動裝配. byName: 若屬性名和某一個 bean 的 id 名一致, 即可完成自動裝配. 若沒有 id 一致的, 則無法完成自動裝配 --> <!-- 在使用 XML 配置時, 自動轉配用的不多. 但在基於 注解 的配置時, 自動裝配使用的較多. --> <bean id="dao" class="com.atguigu.spring.ref.Dao"> <property name="dataSource" value="C3P0"></property> </bean> <!-- 默認情況下 bean 是單例的! --> <!-- 但有的時候, bean 就不能使單例的. 例如: Struts2 的 Action 就不是單例的! 可以通過 scope 屬性來指定 bean 的作用域 --> <!-- prototype: 原型的. 每次調用 getBean 方法都會返回一個新的 bean. 且在第一次調用 getBean 方法時才創建實例 singleton: 單例的. 每次調用 getBean 方法都會返回同一個 bean. 且在 IOC 容器初始化時即創建 bean 的實例. 默認值 --> <bean id="dao2" class="com.atguigu.spring.ref.Dao" scope="prototype"></bean> <bean id="service" class="com.atguigu.spring.ref.Service" autowire="byName"></bean> <bean id="action" class="com.atguigu.spring.ref.Action" autowire="byType"></bean> <!-- 導入外部的資源文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> </bean> <!-- 測試 SpEL: 可以為屬性進行動態的賦值(了解) --> <bean id="girl" class="com.atguigu.spring.helloworld.User"> <property name="userName" value="周迅"></property> </bean> <bean id="boy" class="com.atguigu.spring.helloworld.User" init-method="init" destroy-method="destroy"> <property name="userName" value="高勝遠"></property> <property name="wifeName" value="#{girl.userName}"></property> </bean> <!-- 配置 bean 後置處理器: 不需要配置 id 屬性, IOC 容器會識別到他是一個 bean 後置處理器, 並調用其方法 --> <bean class="com.atguigu.spring.ref.MyBeanPostProcessor"></bean> <!-- 通過工廠方法的方式來配置 bean --> <!-- 1. 通過靜態工廠方法: 一個類中有一個靜態方法, 可以返回一個類的實例(了解) --> <!-- 在 class 中指定靜態工廠方法的全類名, 在 factory-method 中指定靜態工廠方法的方法名 --> <bean id="dateFormat" class="java.text.DateFormat" factory-method="getDateInstance"> <!-- 可以通過 constructor-arg 子節點為靜態工廠方法指定參數 --> <constructor-arg value="2"></constructor-arg> </bean> <!-- 2. 實例工廠方法: 先需要創建工廠對象, 再調用工廠的非靜態方法返回實例(了解) --> <!-- ①. 創建工廠對應的 bean --> <bean id="simpleDateFormat" class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd hh:mm:ss"></constructor-arg> </bean> <!-- ②. 有實例工廠方法來創建 bean 實例 --> <!-- factory-bean 指向工廠 bean, factory-method 指定工廠方法(了解) --> <bean id="datetime" factory-bean="simpleDateFormat" factory-method="parse"> <!-- 通過 constructor-arg 執行調用工廠方法需要傳入的參數 --> <constructor-arg value="1990-12-12 12:12:12"></constructor-arg> </bean> <!-- 配置通過 FactroyBean 的方式來創建 bean 的實例(了解) --> <bean id="user" class="com.atguigu.spring.ref.UserBean"></bean> </beans>
測試Main函數:
package com.atguigu.spring.ref; import java.sql.SQLException; import java.text.DateFormat; import java.util.Date; import javax.sql.DataSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.atguigu.spring.helloworld.User; public class Main { public static void main(String[] args) throws SQLException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml"); //測試 spEL User boy = (User) ctx.getBean("boy"); System.out.println(boy); ctx.close(); } }
測試結果:
說明:首先通過構造函數來創建bean的實例,然後通過setter方法設置屬性,在使用bean實例之前,執行了init初始化方法,使用之後又執行了destroy方法。
Bean後置處理允許在調用初始化方法前後對Bean進行額外的處理,Bean後置處理器對IOC容器的所有Bean實例逐一處理,而非單一實例。其典型應用是:檢查Bean屬性的正確性或根據特定的標准更改Bean的屬性。
對Bean後置處理器而言,需要實現接口BeanPostProcessor,在初始化方法被調用前後,Spring將把每個Bean實例分別傳遞給上述接口的以下兩個方法:
添加Bean後置處理器後,Spring IOC容器對Bean的生命周期管理的過程為:
示例演示Bean的後置處理器:
先定義MyBeanPostProcessor類並實現BeanPostProcessor接口:
package com.atguigu.spring.ref; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import com.atguigu.spring.helloworld.User; public class MyBeanPostProcessor implements BeanPostProcessor { //該方法在 init 方法之後被調用 @Override public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException { if(arg1.equals("boy")){ System.out.println("postProcessAfterInitialization..." + arg0 + "," + arg1); User user = (User) arg0; user.setUserName("李大齊"); } return arg0; } //該方法在 init 方法之前被調用 //可以工作返回的對象來決定最終返回給 getBean 方法的對象是哪一個, 屬性值是什麼 /** * @param arg0: 實際要返回的對象 * @param arg1: bean 的 id 值 */ @Override public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException { if(arg1.equals("boy")) System.out.println("postProcessBeforeInitialization..." + arg0 + "," + arg1); return arg0; } }
bean-auto.xml文件中配置Bean的後置處理器bean,<bean class="com.atguigu.spring.ref.MyBeanPostProcessor"></bean>在上面的配置文件中已標注,不再贅述。
main函數測試類在與上文相同,測試結果如下:
說明:和上面的測試結果對比可以看出,多了一個在bean初始化方法之前將 Bean 實例傳遞給 Bean 後置處理器的 postProcessBeforeInitialization 方法和在執行bean初始化方法之後將 Bean 實例傳遞給 Bean 後置處理器的 postProcessAfterInitialization方法
創建Car實體類:
package com.atguigu.spring.ref; public class Car { private String company; private String brand; private int maxSpeed; private float price; public Car() { } public Car(String company,float price) { super(); this.company = company; this.price = price; } @Override public String toString() { return "Car [company=" + company + ", brand=" + brand + ", maxSpeed=" + maxSpeed + ", price=" + price + "]"; } }
創建靜態工廠方法:
package com.atguigu.spring.ref; import java.util.HashMap; import java.util.Map; public class StaticCarFactory { private static Map<String, Car> cars = new HashMap<String, Car>(); static { cars.put("Audi", new Car("Audi", 300000)); cars.put("Ford", new Car("Ford", 500000)); } // 靜態工廠方法 public static Car getCar(String name) { return cars.get(name); } }
bean-auto.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: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.0.xsd"> <!-- 通過工廠方法的方式來配置 bean --> <!-- 1. 通過靜態工廠方法: 一個類中有一個靜態方法, 可以返回一個類的實例(了解) --> <!-- 在 class 中指定靜態工廠方法的全類名, 在 factory-method 中指定靜態工廠方法的方法名 --> <bean id="car" class="com.atguigu.spring.ref.StaticCarFactory" factory-method="getCar"> <!-- 可以通過 constructor-arg 子節點為靜態工廠方法指定參數 --> <constructor-arg value="Audi"></constructor-arg> </bean> <bean id="dateFormat" class="java.text.DateFormat" factory-method="getDateInstance"> <!-- 可以通過 constructor-arg 子節點為靜態工廠方法指定參數 --> <constructor-arg value="2"></constructor-arg> </bean> <!-- 2. 實例工廠方法: 先需要創建工廠對象, 再調用工廠的非靜態方法返回實例(了解) --> <!-- ①. 創建工廠對應的 bean --> <bean id="simpleDateFormat" class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd hh:mm:ss"></constructor-arg> </bean> <!-- ②. 有實例工廠方法來創建 bean 實例 --> <!-- factory-bean 指向工廠 bean, factory-method 指定工廠方法(了解) --> <bean id="datetime" factory-bean="simpleDateFormat" factory-method="parse"> <!-- 通過 constructor-arg 執行調用工廠方法需要傳入的參數 --> <constructor-arg value="1990-12-12 12:12:12"></constructor-arg> </bean> <!-- 配置通過 FactroyBean 的方式來創建 bean 的實例(了解) --> <bean id="user" class="com.atguigu.spring.ref.UserBean"></bean> </beans>
測試函數:
package com.atguigu.spring.ref; import java.sql.SQLException; import java.text.DateFormat; import java.util.Date; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) throws SQLException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-auto.xml"); Car car = (Car) ctx.getBean("car"); System.out.println(car+"**********"); ctx.close(); } }
執行結果:
實例工廠方法: 將對象的創建過程封裝到另外一個對象實例的方法裡. 當客戶端需要請求對象時, 只需要簡單的調用該實例方法而不需要關心對象的創建細節。
要聲明通過實例工廠方法創建的 Bean
(1).在 bean 的 factory-bean 屬性裡指定擁有該工廠方法的 Bean
(2).在 factory-method 屬性裡指定該工廠方法的名稱
(3).使用 construtor-arg 元素為工廠方法傳遞方法參數
也可以通過實現FactoryBean接口來創建Bean的實例:
創建UserBean並實現FactoryBean接口:
package com.atguigu.spring.ref; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.FactoryBean; import com.atguigu.spring.helloworld.Car; import com.atguigu.spring.helloworld.User; public class UserBean implements FactoryBean<User>{ /** * 返回的 bean 的實例 */ @Override public User getObject() throws Exception { User user = new User(); user.setUserName("abc"); user.setWifeName("ABC"); List<Car> cars = new ArrayList<>(); cars.add(new Car("ShangHai", "BuiKe", 180, 300000)); cars.add(new Car("ShangHai", "CRUZE", 130, 150000)); user.setCars(cars); return user; } /** * 返回的 bean 的類型 */ @Override public Class<?> getObjectType() { return User.class; } /** * 返回的 bean 是否為單例的 */ @Override public boolean isSingleton() { return true; } }
bean-auto.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: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.0.xsd"> <!-- 通過工廠方法的方式來配置 bean --> <!-- 1. 通過靜態工廠方法: 一個類中有一個靜態方法, 可以返回一個類的實例(了解) --> <!-- 在 class 中指定靜態工廠方法的全類名, 在 factory-method 中指定靜態工廠方法的方法名 --> <bean id="car" class="com.atguigu.spring.ref.StaticCarFactory" factory-method="getCar"> <!-- 可以通過 constructor-arg 子節點為靜態工廠方法指定參數 --> <constructor-arg value="Audi"></constructor-arg> </bean> <bean id="dateFormat" class="java.text.DateFormat" factory-method="getDateInstance"> <!-- 可以通過 constructor-arg 子節點為靜態工廠方法指定參數 --> <constructor-arg value="2"></constructor-arg> </bean> <!-- 2. 實例工廠方法: 先需要創建工廠對象, 再調用工廠的非靜態方法返回實例(了解) --> <!-- ①. 創建工廠對應的 bean --> <bean id="simpleDateFormat" class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd hh:mm:ss"></constructor-arg> </bean> <!-- ②. 有實例工廠方法來創建 bean 實例 --> <!-- factory-bean 指向工廠 bean, factory-method 指定工廠方法(了解) --> <bean id="datetime" factory-bean="simpleDateFormat" factory-method="parse"> <!-- 通過 constructor-arg 執行調用工廠方法需要傳入的參數 --> <constructor-arg value="1990-12-12 12:12:12"></constructor-arg> </bean> <!-- 配置通過 FactroyBean 的方式來創建 bean 的實例(了解) --> <bean id="user" class="com.atguigu.spring.ref.UserBean"></bean> </beans>
scope用來聲明容器中的對象所應該處的限定場景或者說該對象的存活時間。
1、singleton
標記為擁有singleton的對象定義,在Spring的IOC容器中只存在一個實例,所有對該對象的引用將共享這個實例。該實例從容器啟動,並因為第一次請求而初始化之後,將一直存活到容器退出。
2、prototype
擁有prototype的bean定義,容器在接到該類型對象的請求的時候,會每次都重新生成一個新的對象實例給請求方,雖然這種類型的對象的實例化以及屬性設置等工作都是由容器負責的,但是只要准備完畢,並且對象實例返回給請求方之後,容器就不在擁有當前返回對象的引用。
3、request、session和global session
這三類只適用於Web應用程序,通常是與XmlWebApplicationContext共同使用。request:XmlWebApplicationContext會為每個http請求創建一個全新的request-processor對象供當前請求使用,當請求結束後,該對象實例的生命周期即告結束。request可以看做是prototype的一種特例,除了應用場景更加具體。
session:Spring容器會為每個獨立的session創建屬於他們自己的全新的對象實例,與request相比,除了擁有更長的存活時間,其他沒什麼差別。
global session:只在基於portlet的web應用程序中才有意義