文章基於 林炳文Evankaka原創作品。(內容略有刪減,加入自己理解,加背景色部分)
摘要:spring的核心容器實現了Ioc,其目 的是提供一種無侵入式的框架。在本文中,首先講解了Spring的基礎bean的相關知識,然後介紹了Spring是如何對bean進行管理的。
在Spring中,有2個最基本最重要的包,即org.springframework.beans 和org.springframework.context.在這兩個包中實現了無侵入式的框架,代碼中大量引用了Java的反射機制,通過動態調用的方式避免了硬編碼,爲spring的反向控制特性提供了基礎。在這2個包中,最重要的類是BeanFactory和ApplicationContext。
- BeanFactory提供了一種先進的配置機制來管理任何種類的bean。是Spring框架的基礎設施面上的功能,是最本質的Spring的基礎。
- ApplicationContext建立在BeanFactory之上,並增加了其他功能,如國際化,獲取資源,事件傳遞等。面向的是使用Spring框架的開發者,計劃所有的場合都適用。
- 因此在大多數場合都採用ApplicationContext方法。
一、Bean的基礎知識
1.在xml配置文件中,bean的標識(id 和 name)
id:指定在benafactory中管理該bean的唯一的標識。name可用來唯一標識bean 或給bean起別名。
- <bean id="helloWorld" class="com.name.HelloWorld">
- ........
- <span style="color:#000000;"> </bean></span>
class屬性指定了bean的來源,即bean的實際路徑。注意要指定全路徑,而不可只寫類名。
3.Bean的作用域
在spring中,bean共有五種作用域:分別是singleton、prototype、request、session、global和global Session。在以上五種作用域中,singleton和prototype是最常用的。
singloeton:只有一個共享的實例存在,所有對這個bean的請求都會返回這個唯一實例。
prototype:對這個bean的每次請求都會都會創建一個新的bean實例。根據已經存在的bean而clone出來的bean。默認爲singleton模式。
改寫成prototype模式寫法如下:
- <bean id="student3" class="com.mucfc.beanfactory.Student" scope="prototype">
- .......
- </bean>
spring中,bean的屬性值有2種注入方式。setter注入和構造函數注入。(此處注意,Ioc中bean的注入方式有三種與這個相區別,分別是Setter方法注入,構造器注入以及接口注入)
setter注入是在調用無參的構造函數或無參的靜態工廠方法實例化配置文檔中定義的bean之後,通過調用bean上的setter方法實現的。
構造函數的依賴注入是通過調用帶有很多參數的構造方法實現的,每個參數表示一個對象或者屬性。
這裏不懂看此文
5.對屬性null值的處理
- <bean id="student5" class="com.mucfc.beanfactory.Student">
- <property name="std_name">
- <value></value>
- </property>
- <property name="std_id">
- <value>2005</value>
- </property>
- </bean>
<bean id="student5" class="com.mucfc.beanfactory.Student">
<property name="std_name">
<value></value>
</property>
<property name="std_id">
<value>2005</value>
</property>
</bean>
或者
- <bean id="student5" class="com.mucfc.beanfactory.Student">
- <property name="std_name">
- <value/>
- </property>
- <property name="std_id">
- <value>2005</value>
- </property>
- </bean>
6.使用依賴depends-on
此屬性可在使用該bean之前,強制初始化一個或多個bean的初始化。例如
- <bean id="school" class="com.mucfc.beanfactory.School"
- depends-on="student6">
- <property name="student" ref="student6" />
- </bean>
- <bean id="student6" class="com.mucfc.beanfactory.Student">
- <property name="std_name" value="水水" />
- <property name="std_id" value="3009" />
- </bean>
- <property name=" xxx" ref="yyyy "/>
或者
- <property name="xxxx">
- <ref bean="yyt"/>
- <property/>
<property name="xxxx">
<ref bean="yyt"/>
<property/>
8、ref local指定同一個xml文件中的引用二、bean的生命週期
beanfactory中bean的生命週期圖
2.1 實例bean
1.當調用者通過getBean(beanName)向容器請求某一個Bean時,如果容器註冊了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在實例化Bean之前,將調用接口的postProcessBeforeInstantiation()方法;
2.根據配置情況調用Bean構造函數或工廠方法實例化Bean;
3.如果容器註冊了InstantiationAwareBeanPostProcessor接口,在實例化Bean之後,調用該接口的postProcessAfterInstantiation()方法,可在這裏對已經實例化的對象進行一些"梳妝打扮";
4.如果Bean配置了屬性信息,容器在這一步着手將配置值設置到Bean對應的屬性中,不過在設置每個屬性之前將先調用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法;
2.2 初始化和使用bean
1:BeanNameAware的setBeanName():
如果Bean類有實現org.springframework.beans.BeanNameAware接口,工廠調用Bean的setBeanName()方法傳遞Bean的ID。
2:BeanFactoryAware的setBeanFactory():
如果Bean類有實現org.springframework.beans.factory.BeanFactoryAware接口,工廠調用setBeanFactory()方法傳入工廠自身。
3:BeanPostProcessors的ProcessBeforeInitialization()
如果有org.springframework.beans.factory.config.BeanPostProcessors和Bean關聯,那麼其postProcessBeforeInitialization()方法將被將被調用。
4:initializingBean的afterPropertiesSet():
如果Bean類已實現org.springframework.beans.factory.InitializingBean接口,則執行他的afterProPertiesSet()方法
5:Bean定義文件中定義init-method:
可以在Bean定義文件中使用"init-method"屬性設定方法名稱例如:
如果有以上設置的話,則執行到這個階段,就會執行initBean()方法
6:BeanPostProcessors的ProcessaAfterInitialization()
如果有任何的BeanPostProcessors實例與Bean實例關聯,則執行BeanPostProcessors實例的ProcessaAfterInitialization()方法
BeanPostProcessor後處理器定義了兩個方法:
其一是postProcessBeforeInitialization()在第8步調用;其二是Object postProcessAfterInitialization(Object bean, String beanName)方法,這個方法在此時調用,容器再次獲得對Bean進行加工處理的機會;
如果在<bean>中指定Bean的作用範圍爲scope="prototype",將Bean返回給調用者,調用者負責Bean後續生命的管理,Spring不再管理這個Bean的生命週期。如果作用範圍設置爲scope="singleton",則將Bean放入到Spring IoC容器的緩存池中,並將Bean引用返回給調用者,Spring繼續對這些Bean進行後續的生命管理;
2.2 銷燬bean此時,Bean已經可以被應用系統使用,並且將保留在BeanFactory中知道它不在被使用。有兩種方法可以將其從BeanFactory中刪除掉
1:DisposableBean的destroy()
在容器關閉時,如果Bean類有實現org.springframework.beans.factory.DisposableBean接口,則執行他的destroy()方法
2:Bean定義文件中定義destroy-method
在容器關閉時,可以在Bean定義文件中使用"destroy-method"屬性設定方法名稱,例如:
如果有以上設定的話,則進行至這個階段時,就會執行destroy()方法,如果是使用ApplicationContext來生成並管理Bean的話則稍有不同,使用ApplicationContext來生成及管理Bean實例的話,在執行BeanFactoryAware的setBeanFactory()階段後,若Bean類上有實現org.springframework.context.ApplicationContextAware接口,則執行其setApplicationContext()方法,接着才執行BeanPostProcessors的ProcessBeforeInitialization()及之後的流程。
三、Beanfactory中bean生命週期實例
下面我們來看看一個實例吧,自己新建一個工程,導入需要的Spring包,然後建立如下Worker.java(這裏是bean初始化和使用時會用到的函數 )- package com.mucfc.beanlive;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.beans.factory.BeanFactoryAware;
- import org.springframework.beans.factory.BeanNameAware;
- import org.springframework.beans.factory.DisposableBean;
- import org.springframework.beans.factory.InitializingBean;
- //①管理Bean生命週期的接口
- public class Worker implements BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean{
- private String name;
- private String workType;
- private int salary;
- public BeanFactory beanFactory;
- private String beanName;
- public void Worker(){
- System.out.println("調用worker()構造函數");
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getWorkType() {
- return workType;
- }
- public void setWorkType(String workType) {
- this.workType = workType;
- }
- public int getSalary() {
- return salary;
- }
- public void setSalary(int salary) {
- this.salary = salary;
- }
- public void printInfo() {
- System.out.println("name:" + name + ";workType:" + workType + ";salary:"
- + salary);
- }
- //⑤DisposableBean接口方法
- @Override
- public void destroy() throws Exception {
- System.out.println("----------------銷燬bean----------------------");
- System.out.println("調用DisposableBean.destroy()。");
- }
- //④InitializingBean接口方法
- @Override
- public void afterPropertiesSet() throws Exception {
- System.out.println("調用InitializingBean.afterPropertiesSet()。");
- }
- //③BeanNameAware接口方法
- @Override
- public void setBeanName(String arg0) {
- System.out.println("----------------初始化bean----------------------");
- System.out.println("調用BeanNameAware.setBeanName()。");
- this.beanName = arg0;
- }
- //②BeanFactoryAware接口方法
- @Override
- public void setBeanFactory(BeanFactory arg0) throws BeansException {
- System.out.println("調用BeanFactoryAware.setBeanFactory()。");
- this.beanFactory = arg0;
- }
- //⑥通過<bean>的init-method屬性指定的初始化方法
- public void myInit() {
- System.out.println("調用init-method所指定的myInit(),將salary設置爲600。");
- this.salary = 600;
- }
- //⑦通過<bean>的destroy-method屬性指定的銷燬方法
- public void myDestroy() {
- System.out.println("調用destroy-method所指定的myDestroy()。");
- }
- }
MyInstantiationAwareBeanPostProcessor通過擴展InstantiationAwareBeanPostProcessor適配器InstantiationAwareBeanPostProcessorAdapter提供實現:
(這是實例化bean時會用到的函數)
- package com.mucfc.beanlive;
- import java.beans.PropertyDescriptor;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.PropertyValues;
- import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
- public class MyInstantiationAwareBeanPostProcessor extends
- InstantiationAwareBeanPostProcessorAdapter {
- // ①接口方法:在實例化Bean前進行調用
- public Object postProcessBeforeInstantiation(Class beanClass,
- String beanName) throws BeansException {
- System.out.println("----------------實例化bean----------------------");
- // ①-1僅對容器中worker Bean進行處理
- if ("worker".equals(beanName)) {
- System.out
- .println("InstantiationAware BeanPostProcessor. postProcess BeforeInstantiation");
- }
- return null;
- }
- // ②接口方法:在實例化Bean後調用
- public boolean postProcessAfterInstantiation(Object bean, String beanName)
- throws BeansException {
- // ②-1僅對容器中car Bean進行處理
- if ("worker".equals(beanName)) {
- System.out
- .println("InstantiationAware BeanPostProcessor.postProcess AfterInstantiation");
- }
- return true;
- }
- // ③接口方法:在設置某個屬性時調用
- public PropertyValues postProcessPropertyValues(PropertyValues pvs,
- PropertyDescriptor[] pds, Object bean, String beanName)
- throws BeansException {
- // ③-1僅對容器中wroker Bean進行處理,還可以通過pdst入參進行過濾,
- // 僅對car的某個特定屬性時進行處理。
- if ("worker".equals(beanName)) {
- System.out
- .println("Instantiation AwareBeanPostProcessor.postProcess PropertyValues");
- }
- return pvs;
- }
- }
此外,我們還提供了一個BeanPostProcessor實現類,在該實現類中,我們也只對work Bean進行處理,對配置文件所提供的屬性設置值進行判斷,並執行相應的"補缺補漏"的操作:
BeanPostProcessor實現類(這裏是bean初始化和使用時會用到的函數 )
- package com.mucfc.beanlive;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.config.BeanPostProcessor;
- public class MyBeanPostProcessor implements BeanPostProcessor{
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName)
- throws BeansException {
- if(beanName.equals("worker")){
- Worker worker = (Worker)bean;
- if(worker.getWorkType() == null){
- System.out.println("調用BeanPostProcessor.postProcess AfterInitialization(), getWorkType爲空,設置爲默認臨時工");
- worker.setWorkType("臨時工");
- }
- }
- return bean;
- }
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName)
- throws BeansException {
- if(beanName.equals("worker")){
- Worker worker = (Worker)bean;
- if(worker.getSalary() >= 1000){
- System.out.println("調用BeanPostProcessor.postProcess BeforeInitialization(), 將salary調整爲800。");
- worker.setSalary(800);
- }
- }
- return bean;
- }
- }
至於如何將MyInstantiationAwareBeanPostProcessor和MyBeanPostProcessor這兩個後處理器註冊到BeanFactory容器中
現在,我們在Spring配置文件中定義Car的配置信息,如代碼清單3 29所示:
然後就是beans.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:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
- <bean id="worker" class="com.mucfc.beanlive.Worker"
- init-method="myInit"
- destroy-method="myDestroy"
- p:name="小強"
- p:salary="1000"
- />
- </beans>
<?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.2.xsd">
<bean id="worker" class="com.mucfc.beanlive.Worker"
init-method="myInit"
destroy-method="myDestroy"
p:name="小強"
p:salary="1000"
/>
</beans>
使用方法:
- package com.mucfc.beanlive;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.beans.factory.config.ConfigurableBeanFactory;
- import org.springframework.beans.factory.xml.XmlBeanFactory;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.core.io.Resource;
- public class Test {
- private static void LifeCycleInBeanFactory(){
- //①下面兩句裝載配置文件並啓動容器
- Resource res = new ClassPathResource("beans.xml");
- BeanFactory bf = new XmlBeanFactory(res);
- //②向容器中註冊MyBeanPostProcessor後處理器
- ((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyBeanPostProcessor());
- //③向容器中註冊MyInstantiationAwareBeanPostProcessor後處理器
- ((ConfigurableBeanFactory)bf).addBeanPostProcessor(
- new MyInstantiationAwareBeanPostProcessor());
- //④第一次從容器中獲取worker,將觸發容器實例化該Bean,這將引發Bean生命週期方法的調用。
- Worker worker1 = (Worker)bf.getBean("worker");
- System.out.println("第一次從容器中獲取worker");
- worker1.printInfo();
- System.out.println("修改第一次從容器中獲取worker的workType");
- worker1.setWorkType("正式工");
- System.out.println("第一次從容器中獲取worker(修改過後的)");
- worker1.printInfo();
- //⑤第二次從容器中獲取worker,直接從緩存池中獲取
- System.out.println("第二次從容器中獲取worker");
- Worker worker2= (Worker)bf.getBean("worker");
- worker2.printInfo();
- //⑥查看worker1和worker2是否指向同一引用
- System.out.println("查看worker1和worker2是否指向同一引用 ");
- System.out.println("worker1==worker2:"+(worker1==worker2));
- //⑦關閉容器
- ((XmlBeanFactory)bf).destroySingletons();
- }
- public static void main(String[] args) {
- LifeCycleInBeanFactory();
- }
- }
我們可以看到第二次獲取worker時,直接從容器的緩存中獲取,它們兩個的指向的是同一個引用!切記!
四、ApplicationContext與beanfactory的區別
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
總結:日常使用中多采用的是ApplicationContext方法,由於其實現的方法較爲簡易,且功能更加強大,因此在使用中應儘量採取該方法。