SSM搭建-Spring中的Bean(3)

         文章基於 林炳文Evankaka原創作品。(內容略有刪減,加入自己理解,加背景色部分)

         摘要:spring的核心容器實現了Ioc,其目 的是提供一種無侵入式的框架。在本文中,首先講解了Spring的基礎bean的相關知識,然後介紹了Spring是如何對bean進行管理的。

        在Spring中,有2個最基本最重要的包,即org.springframework.beans 和org.springframework.context.在這兩個包中實現了無侵入式的框架,代碼中大量引用了Java的反射機制,通過動態調用的方式避免了硬編碼,爲spring的反向控制特性提供了基礎。在這2個包中,最重要的類是BeanFactoryApplicationContext

  • BeanFactory提供了一種先進的配置機制來管理任何種類的bean。是Spring框架的基礎設施面上的功能,是最本質的Spring的基礎。
  • ApplicationContext建立在BeanFactory之上,並增加了其他功能,如國際化,獲取資源,事件傳遞等。面向的是使用Spring框架的開發者,計劃所有的場合都適用。
  • 因此在大多數場合都採用ApplicationContext方法。

 本文工程免費下載

一、Bean的基礎知識

1.在xml配置文件中,bean的標識(id 和 name)

 id:指定在benafactory中管理該bean的唯一的標識。name可用來唯一標識bean 或給bean起別名。

  1.  <bean id="helloWorld" class="com.name.HelloWorld">  
  2. ........  
  3. <span style="color:#000000;"> </bean></span>  
2.bean的class

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模式寫法如下:

  1. <bean id="student3" class="com.mucfc.beanfactory.Student" scope="prototype">  
  2.     .......  
  3. </bean>  
4.bean的屬性

spring中,bean的屬性值有2種注入方式。setter注入和構造函數注入。(此處注意,Ioc中bean的注入方式有三種與這個相區別,分別是Setter方法注入,構造器注入以及接口注入)

setter注入是在調用無參的構造函數或無參的靜態工廠方法實例化配置文檔中定義的bean之後,通過調用bean上的setter方法實現的。

構造函數的依賴注入是通過調用帶有很多參數的構造方法實現的,每個參數表示一個對象或者屬性。

這裏不懂看此文

 5.對屬性null值的處理

  1. <bean id="student5" class="com.mucfc.beanfactory.Student">  
  2.     <property name="std_name">  
  3.         <value></value>  
  4.     </property>  
  5.     <property name="std_id">  
  6.         <value>2005</value>  
  7.     </property>  
  8. </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>
或者
  1. <bean id="student5" class="com.mucfc.beanfactory.Student">  
  2.     <property name="std_name">  
  3.         <value/>  
  4.     </property>  
  5.     <property name="std_id">  
  6.         <value>2005</value>  
  7.     </property>  
  8. </bean>  
以上等同於this.std_name=null

 6.使用依賴depends-on

此屬性可在使用該bean之前,強制初始化一個或多個bean的初始化。例如

  1. <bean id="school" class="com.mucfc.beanfactory.School"  
  2.     depends-on="student6">  
  3.     <property name="student" ref="student6" />  
  4. </bean>  
其中student6如下:
  1. <bean id="student6" class="com.mucfc.beanfactory.Student">  
  2.         <property name="std_name" value="水水" />  
  3.         <property name="std_id" value="3009" />  
  4.     </bean>  
7.指定bean引用

  1. <property name=" xxx" ref="yyyy "/>  

或者

  1. <property name="xxxx">  
  2. <ref bean="yyt"/>  
  3. <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初始化和使用時會用到的函數 )
  1. package com.mucfc.beanlive;  
  2.   
  3. import org.springframework.beans.BeansException;  
  4. import org.springframework.beans.factory.BeanFactory;  
  5. import org.springframework.beans.factory.BeanFactoryAware;  
  6. import org.springframework.beans.factory.BeanNameAware;  
  7. import org.springframework.beans.factory.DisposableBean;  
  8. import org.springframework.beans.factory.InitializingBean;  
  9.   
  10. //①管理Bean生命週期的接口     
  11. public class Worker implements BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean{  
  12.     private String name;  
  13.     private String workType;  
  14.     private int salary;  
  15.       
  16.     public BeanFactory beanFactory;  
  17.     private String beanName;  
  18.       
  19.     public void Worker(){  
  20.         System.out.println("調用worker()構造函數");  
  21.     }  
  22.       
  23.     public String getName() {  
  24.         return name;  
  25.     }  
  26.     public void setName(String name) {  
  27.         this.name = name;  
  28.     }  
  29.     public String getWorkType() {  
  30.         return workType;  
  31.     }  
  32.     public void setWorkType(String workType) {  
  33.         this.workType = workType;  
  34.     }  
  35.     public int getSalary() {  
  36.         return salary;  
  37.     }  
  38.     public void setSalary(int salary) {  
  39.         this.salary = salary;  
  40.     }  
  41.      public void printInfo() {      
  42.             System.out.println("name:" + name + ";workType:" + workType + ";salary:"      
  43.                     + salary);      
  44.         }      
  45.      //⑤DisposableBean接口方法      
  46.     @Override  
  47.     public void destroy() throws Exception {  
  48.         System.out.println("----------------銷燬bean----------------------");  
  49.          System.out.println("調用DisposableBean.destroy()。");      
  50.           
  51.     }  
  52.      //④InitializingBean接口方法      
  53.     @Override  
  54.     public void afterPropertiesSet() throws Exception {  
  55.          System.out.println("調用InitializingBean.afterPropertiesSet()。");     
  56.           
  57.     }  
  58.      //③BeanNameAware接口方法      
  59.     @Override  
  60.     public void setBeanName(String arg0) {  
  61.         System.out.println("----------------初始化bean----------------------");  
  62.          System.out.println("調用BeanNameAware.setBeanName()。");      
  63.             this.beanName = arg0;      
  64.           
  65.     }  
  66.      //②BeanFactoryAware接口方法      
  67.     @Override  
  68.     public void setBeanFactory(BeanFactory arg0) throws BeansException {  
  69.          System.out.println("調用BeanFactoryAware.setBeanFactory()。");      
  70.          this.beanFactory = arg0;      
  71.     }  
  72.       
  73.     //⑥通過<bean>的init-method屬性指定的初始化方法      
  74.     public void myInit() {      
  75.         System.out.println("調用init-method所指定的myInit(),將salary設置爲600。");      
  76.         this.salary = 600;      
  77.     }      
  78.           
  79.      //⑦通過<bean>的destroy-method屬性指定的銷燬方法      
  80.     public void myDestroy() {      
  81.         System.out.println("調用destroy-method所指定的myDestroy()。");      
  82.     }      
  83.       
  84. }  
Worker類在②、③、④、⑤處實現了BeanFactoryAware、BeanNameAware、InitializingBean、DisposableBean這些Bean級的生命週期控制接口;在⑥和⑦處定義了myInit()和myDestroy()方法,以便在配置文件中通過init-method和destroy-method屬性定義初始化和銷燬方法。

MyInstantiationAwareBeanPostProcessor通過擴展InstantiationAwareBeanPostProcessor適配器InstantiationAwareBeanPostProcessorAdapter提供實現:

(這是實例化bean時會用到的函數)

  1. package com.mucfc.beanlive;  
  2.   
  3. import java.beans.PropertyDescriptor;  
  4.   
  5. import org.springframework.beans.BeansException;  
  6. import org.springframework.beans.PropertyValues;  
  7. import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;  
  8.   
  9. public class MyInstantiationAwareBeanPostProcessor extends  
  10.         InstantiationAwareBeanPostProcessorAdapter {  
  11.     // ①接口方法:在實例化Bean前進行調用  
  12.     public Object postProcessBeforeInstantiation(Class beanClass,  
  13.             String beanName) throws BeansException {  
  14.         System.out.println("----------------實例化bean----------------------");  
  15.         // ①-1僅對容器中worker Bean進行處理  
  16.         if ("worker".equals(beanName)) {  
  17.             System.out  
  18.                     .println("InstantiationAware BeanPostProcessor. postProcess BeforeInstantiation");  
  19.         }  
  20.         return null;  
  21.     }  
  22.   
  23.     // ②接口方法:在實例化Bean後調用  
  24.     public boolean postProcessAfterInstantiation(Object bean, String beanName)  
  25.             throws BeansException {  
  26.         // ②-1僅對容器中car Bean進行處理  
  27.         if ("worker".equals(beanName)) {  
  28.             System.out  
  29.                     .println("InstantiationAware BeanPostProcessor.postProcess AfterInstantiation");  
  30.         }  
  31.         return true;  
  32.     }  
  33.   
  34.     // ③接口方法:在設置某個屬性時調用  
  35.     public PropertyValues postProcessPropertyValues(PropertyValues pvs,  
  36.             PropertyDescriptor[] pds, Object bean, String beanName)  
  37.             throws BeansException {  
  38.   
  39.         // ③-1僅對容器中wroker Bean進行處理,還可以通過pdst入參進行過濾,  
  40.         // 僅對car的某個特定屬性時進行處理。  
  41.         if ("worker".equals(beanName)) {  
  42.             System.out  
  43.                     .println("Instantiation AwareBeanPostProcessor.postProcess PropertyValues");  
  44.         }  
  45.         return pvs;  
  46.     }  
  47. }  
在MyInstantiationAwareBeanPostProcessor中,我們通過過濾條件僅對work Bean進行處理,而對其他的Bean一概視而不見。

此外,我們還提供了一個BeanPostProcessor實現類,在該實現類中,我們也只對work Bean進行處理,對配置文件所提供的屬性設置值進行判斷,並執行相應的"補缺補漏"的操作:

BeanPostProcessor實現類(這裏是bean初始化和使用時會用到的函數 )

  1. package com.mucfc.beanlive;  
  2.   
  3. import org.springframework.beans.BeansException;  
  4. import org.springframework.beans.factory.config.BeanPostProcessor;  
  5.   
  6. public class MyBeanPostProcessor implements BeanPostProcessor{  
  7.   
  8.     @Override  
  9.     public Object postProcessAfterInitialization(Object bean, String beanName)  
  10.             throws BeansException {  
  11.          if(beanName.equals("worker")){      
  12.              Worker worker = (Worker)bean;      
  13.                 if(worker.getWorkType() == null){      
  14.                     System.out.println("調用BeanPostProcessor.postProcess AfterInitialization(),  getWorkType爲空,設置爲默認臨時工");      
  15.                     worker.setWorkType("臨時工");   
  16.                 }      
  17.             }      
  18.             return bean;      
  19.     }  
  20.   
  21.     @Override  
  22.     public Object postProcessBeforeInitialization(Object bean, String beanName)  
  23.             throws BeansException {  
  24.         if(beanName.equals("worker")){      
  25.              Worker worker = (Worker)bean;       
  26.                 if(worker.getSalary() >= 1000){      
  27.                     System.out.println("調用BeanPostProcessor.postProcess BeforeInitialization(), 將salary調整爲800。");      
  28.                     worker.setSalary(800);     
  29.                 }      
  30.             }      
  31.             return bean;      
  32.         }      
  33.   
  34. }  
在MyBeanPostProcessor類的postProcessBeforeInitialization()方法中,我們首先判斷處理的Bean是否名爲worker,如果是,進一步判斷該Bean的workerType屬性是否爲空,如果爲空,將該屬性設置爲"臨時工"。在postProcessAfterInitialization()方法中,我們也是隻對名爲woker的Bean進行處理,判斷其salary是否超過最大速度1000,如果超過,將其設置爲800。(黑心老闆啊!!!!!)

至於如何將MyInstantiationAwareBeanPostProcessor和MyBeanPostProcessor這兩個後處理器註冊到BeanFactory容器中

現在,我們在Spring配置文件中定義Car的配置信息,如代碼清單3 29所示:

然後就是beans.xml

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">  
  5. <bean id="worker" class="com.mucfc.beanlive.Worker"  
  6.       init-method="myInit"     
  7.       destroy-method="myDestroy"     
  8.        p:name="小強"     
  9.        p:salary="1000"       
  10. />  
  11. </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>
使用方法:
  1. package com.mucfc.beanlive;  
  2.   
  3. import org.springframework.beans.factory.BeanFactory;  
  4. import org.springframework.beans.factory.config.ConfigurableBeanFactory;  
  5. import org.springframework.beans.factory.xml.XmlBeanFactory;  
  6. import org.springframework.core.io.ClassPathResource;  
  7. import org.springframework.core.io.Resource;  
  8.   
  9. public class Test {  
  10.      private static void LifeCycleInBeanFactory(){    
  11.          //①下面兩句裝載配置文件並啓動容器      
  12.           Resource res = new ClassPathResource("beans.xml");        
  13.           BeanFactory bf = new XmlBeanFactory(res);      
  14.                  
  15.          
  16.           //②向容器中註冊MyBeanPostProcessor後處理器      
  17.     ((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyBeanPostProcessor());      
  18.                 
  19.           //③向容器中註冊MyInstantiationAwareBeanPostProcessor後處理器      
  20.     ((ConfigurableBeanFactory)bf).addBeanPostProcessor(      
  21.                                    new MyInstantiationAwareBeanPostProcessor());      
  22.           //④第一次從容器中獲取worker,將觸發容器實例化該Bean,這將引發Bean生命週期方法的調用。      
  23.           Worker worker1 = (Worker)bf.getBean("worker");      
  24.           System.out.println("第一次從容器中獲取worker");  
  25.           worker1.printInfo();      
  26.           System.out.println("修改第一次從容器中獲取worker的workType");  
  27.           worker1.setWorkType("正式工");     
  28.           System.out.println("第一次從容器中獲取worker(修改過後的)");  
  29.           worker1.printInfo();      
  30.            //⑤第二次從容器中獲取worker,直接從緩存池中獲取      
  31.           System.out.println("第二次從容器中獲取worker");  
  32.           Worker worker2= (Worker)bf.getBean("worker");        
  33.           worker2.printInfo();    
  34.            //⑥查看worker1和worker2是否指向同一引用      
  35.           System.out.println("查看worker1和worker2是否指向同一引用    ");  
  36.           System.out.println("worker1==worker2:"+(worker1==worker2));    
  37.            //⑦關閉容器      
  38.           ((XmlBeanFactory)bf).destroySingletons();      
  39.             
  40.            
  41.      }  
  42.     public static void main(String[] args) {  
  43.         LifeCycleInBeanFactory();  
  44.     }  
  45.   
  46. }  
輸出結果:


我們可以看到第二次獲取worker時,直接從容器的緩存中獲取,它們兩個的指向的是同一個引用!切記!

四、ApplicationContext與beanfactory的區別

從ApplicationContext應用上下文容器中獲取bean和從bean工廠容器中獲取bean的區別?
具體案例:
1、從ApplicationContext中獲取bean
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
當我們去實例化beans.xml,該文件中配置的bean被實例化,(該bean的scope是singleton(單例))
2、如果我們使用beanfactory去獲取bean,當你只是實例化該容器,那麼容器的bean不被實例化,只有當你去使用getBean某個bean時(感謝hemi1995指出錯別字),纔會實時的創建
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
factory.getBean("student");

結論:
1、如果使用ApplicationContext,則配置的bean如果是singleton(單例)不管你用不用都被實例化,好處就是可以預先加載,壞處是浪費內存,
2、如果是用BeanFactory,則當你實例化該對象的時候,配置的bean不會被馬上實例化,當你使用的時候纔會被實例化(就像延緩加載機制)好處就是節約內存,缺點是速度回降慢,
3規則:一般沒有特殊的要求,應當使用ApplicationContext完成(90%以上的項目)

使用ApplicationContext三種經常用到的實現:
1、ClassPathXmlApplicationContext->從類路徑加載
2、FileSystemXmlApplicationContext->從文件系統加載
舉例:ApplicationContext ac = newFIleSystemXmlApplicationContext("C:\Users\Workspaces\MyEclipse 
8.5\myspring\src\com\zqx\inter\beans.xml");
3、XmlWebApplicationContext->從web系統中加載(當tomcat啓動的時候加載)


總結:日常使用中多采用的是ApplicationContext方法,由於其實現的方法較爲簡易,且功能更加強大,因此在使用中應儘量採取該方法。


 本文工程免費下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章