Spring是一款很是強大的框架,能夠說是幾乎全部的企業級Java項目使用了Spring,而Bean又是Spring框架的核心。
Spring框架運用了很是多的設計模式,從總體上看,它的設計嚴格遵循了OCP----開閉原則,即:
一、保證對修改關閉,即外部沒法修改Spring整個運做的流程
二、提供對擴展開放,便可以經過繼承、實現Spring提供的衆多抽象類與接口來改變類加載的行爲
開卷有益,閱讀Spring源碼(無需每一個類都看得很細,大致流程能梳理出來便可)對於我的水平的提高是幫助很是大的,同時也能在工做中即便發現和解決一些不常見的Spring問題。
不過,本文的目的不是整理Spring的流程,而是經過介紹一些經常使用的Spring Bean工具類,來讓咱們能夠更好地使用Spring提供給開發者的多種特性,下面讓咱們開始吧。
html
InitialingBean是一個接口,提供了一個惟一的方法afterPropertiesSet()。
DisposableBean也是一個接口,提供了一個惟一的方法destory()。
這兩個接口是一組的,功能相似,所以放在一塊兒:前者顧名思義在Bean屬性都設置完畢後調用afterPropertiesSet()方法作一些初始化的工做,後者在Bean生命週期結束前調用destory()方法作一些收尾工做。下面看一下例子,爲了能明確地知道afterPropertiesSet()方法的調用時機,加上一個屬性,給屬性set方法,在set方法中打印一些內容:java
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public class LifecycleBean implements InitializingBean, DisposableBean { @SuppressWarnings("unused") private String lifeCycleBeanName; public void setLifeCycleBeanName(String lifeCycleBeanName) { System.out.println("Enter LifecycleBean.setLifeCycleBeanName(), lifeCycleBeanName = " + lifeCycleBeanName); this.lifeCycleBeanName = lifeCycleBeanName; } public void destroy() throws Exception { System.out.println("Enter LifecycleBean.destroy()"); } public void afterPropertiesSet() throws Exception { System.out.println("Enter LifecycleBean.afterPropertiesSet()"); } public void beanStart() { System.out.println("Enter LifecycleBean.beanStart()"); } public void beanEnd() { System.out.println("Enter LifecycleBean.beanEnd()"); } }
配置一個spring.xml:spring
<?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-4.1.xsd"> <bean id="lifeCycleBean" class="org.xrq.bean.lifecycle.LifecycleBean"> <property name="lifeCycleBeanName" value="lifeCycleBean" /> </bean> </beans>
啓動Spring容器,LifecycleBean執行的結果爲:
Enter LifecycleBean.setLifeCycleBeanName(), lifeCycleBeanName = lifeCycleBean
Enter LifecycleBean.afterPropertiesSet()
Enter LifecycleBean.beanStart()
Enter LifecycleBean.destroy()
Enter LifecycleBean.beanEnd()
執行結果和咱們想的同樣,afterPropertiesSet()方法就如同它的名字所表示的那樣,是在Bean的屬性都被設置完畢以後,纔會調用。
關於這兩個接口,我總結幾點:
一、InitializingBean接口、Disposable接口能夠和init-method、destory-method配合使用,接口執行順序優先於配置
二、InitializingBean接口、Disposable接口底層使用類型強轉.方法名()進行直接方法調用,init-method、destory-method底層使用反射,前者和Spring耦合程度更高但效率高,後者解除了和Spring之間的耦合可是效率低,使用哪一個看我的喜愛
三、afterPropertiesSet()方法是在Bean的屬性設置以後纔會進行調用,某個Bean的afterPropertiesSet()方法執行完畢纔會執行下一個Bean的afterPropertiesSet()方法,所以不建議在afterPropertiesSet()方法中寫處理時間太長的方法
設計模式
這三個接口放在一塊兒寫,是由於它們是一組的,做用類似。
"Aware"的意思是"感知到的",那麼這三個接口的意思也不難理解:
一、實現BeanNameAware接口的Bean,在Bean加載的過程當中能夠獲取到該Bean的id
二、實現ApplicationContextAware接口的Bean,在Bean加載的過程當中能夠獲取到Spring的ApplicationContext,這個尤爲重要,ApplicationContext是Spring應用上下文,從ApplicationContext中能夠獲取包括任意的Bean在內的大量Spring容器內容和信息
三、實現BeanFactoryAware接口的Bean,在Bean加載的過程當中能夠獲取到加載該Bean的BeanFactory
看一下例子:
app
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public class AwareBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware { private String beanName; private ApplicationContext applicationContext; private BeanFactory beanFactory; public void setBeanName(String beanName) { System.out.println("Enter AwareBean.setBeanName(), beanName = " + beanName + "\n"); this.beanName = beanName; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("Enter AwareBean.setApplicationContext(), applicationContext = " + applicationContext + "\n"); this.applicationContext = applicationContext; } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("Enter AwareBean.setBeanFactory(), beanfactory = " + beanFactory + "\n"); this.beanFactory = beanFactory; } }
配置一個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-4.1.xsd"> <bean id="AwareBean" class="org.xrq.bean.aware.AwareBean" /> </beans>
啓動Spring容器後的執行結果爲:
Enter AwareBean.setBeanName(), beanName = AwareBean函數
Enter AwareBean.setBeanFactory(), beanfactory = org.springframework.beans.factory.support.DefaultListableBeanFactory@2747fda0: defining beans [AwareBean,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor]; root of factory hierarchy工具
Enter AwareBean.setApplicationContext(), applicationContext = org.springframework.context.support.GenericApplicationContext@5514cd80: startup date [Mon Aug 08 19:23:30 CST 2016]; root of context hierarchypost
關於這三個接口以及上面的打印信息,總結幾點:
一、若是你的BeanName、ApplicationContext、BeanFactory有用,那麼就本身定義一個變量將它們保存下來,若是沒用,那麼只須要實現setXXX()方法,用一下Spring注入進來的參數便可
二、若是Bean同時還實現了InitializingBean,容器會保證BeanName、ApplicationContext和BeanFactory在調用afterPropertiesSet()方法被注入
測試
FactoryBean在Spring中是很是有用的,使用Eclipse/MyEclipse的朋友能夠對FactoryBean使用ctrl+t查看一下,FactoryBean這個接口在Spring容器中有大量的子實現。
傳統的Spring容器加載一個Bean的整個過程,都是由Spring控制的,換句話說,開發者除了設置Bean相關屬性以外,是沒有太多的自主權的。FactoryBean改變了這一點,開發者能夠個性化地定製本身想要實例化出來的Bean,方法就是實現FactoryBean接口。
看一下代碼例子,爲了講清楚FactoryBean,內容相對多一些,首先定義一個接口Animal:
public interface Animal { public void move(); }
定義兩個實現類Monkey和Tiger:
public class Monkey implements Animal { public void move() { System.out.println("Monkey move!"); } }
public class Tiger implements Animal { public void move() { System.out.println("Tiger move!"); } }
寫一個實現類,實現FactoryBean接口:
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public class AnimalFactoryBean implements FactoryBean<Animal> { private String animal; public Animal getObject() throws Exception { if ("Monkey".equals(animal)) { return new Monkey(); } else if ("Tiger".equals(animal)) { return new Tiger(); } else { return null; } } public Class<?> getObjectType() { return Animal.class; } public boolean isSingleton() { return true; } public void setAnimal(String animal) { this.animal = animal; } }
配置一個spring.xml,注入屬性Tiger:
<?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-4.1.xsd"> <bean id="animal" class="org.xrq.bean.factory.AnimalFactoryBean"> <property name="animal" value="Tiger"/> </bean> </beans>
寫一個JUnit的測試類:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath*:spring.xml", }) public class BaseTest { @Resource private Animal animal; @Test public void aa() { animal.move(); } }
查看一下運行結果:
Tiger move!
看到最後獲得的並非FactoryBean自己,而是FactoryBean的泛型對象,這就是FactoryBean的做用。FactoryBean的幾個方法:
一、getObject()方法是最重要的,控制Bean的實例化過程
二、getObjectType()方法獲取接口返回的實例的class
三、isSingleton()方法獲取該Bean是否爲一個單例的Bean
像我這段代碼的功能就是傳入一個String類型的參數,能夠動態控制生成出來的是接口的哪一種子類。有了FactoryBean,一樣的咱們也能夠靈活地操控Bean的生成。
以前的InitializingBean、DisposableBean、FactoryBean包括init-method和destory-method,針對的都是某個Bean控制其初始化的操做,而彷佛沒有一種辦法能夠針對每一個Bean的生成先後作一些邏輯操做,PostProcessor則幫助咱們作到了這一點,先看一個簡單的BeanPostProcessor。
網上有一張圖畫了Bean生命週期的過程,畫得挺好,原圖出處:
BeanPostProcess接口有兩個方法,均可以見名知意:
一、postProcessBeforeInitialization:在初始化Bean以前
二、postProcessAfterInitialization:在初始化Bean以後
值得注意的是,這兩個方法是有返回值的,不要返回null,不然getBean的時候拿不到對象。
寫一段測試代碼,首先定義一個普通的Bean,爲了後面能區分,給Bean加一個屬性:
public class CommonBean { private String commonName; public void setCommonName(String commonName) { this.commonName = commonName; } public void initMethod() { System.out.println("Enter CommonBean.initMethod(), commonName = " + commonName); } }
定義一個PostProcess,實現BeanPostProcess接口:
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public class PostProcessorBean implements BeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Enter ProcessorBean.postProcessAfterInitialization()\n"); return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("Enter ProcessorBean.postProcessBeforeInitialization()"); return bean; } }
配置一個spring.xml,給CommonBean的commonName賦予不一樣的值以區分:
<?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-4.1.xsd"> <bean id="common0" class="org.xrq.bean.common.CommonBean" init-method="initMethod"> <property name="commonName" value="common0"/> </bean> <bean id="common1" class="org.xrq.bean.common.CommonBean" init-method="initMethod"> <property name="commonName" value="common1"/> </bean> <bean id="postProcessorBean" class="org.xrq.bean.processor.PostProcessorBean" /> </beans>
運行一個Spring容器, 初始化結果爲:
Enter ProcessorBean.postProcessBeforeInitialization()
Enter CommonBean.initMethod(), commonName = common0
Enter ProcessorBean.postProcessAfterInitialization()
Enter ProcessorBean.postProcessBeforeInitialization()
Enter CommonBean.initMethod(), commonName = common1
Enter ProcessorBean.postProcessAfterInitialization()
Enter ProcessorBean.postProcessBeforeInitialization()
Enter ProcessorBean.postProcessAfterInitialization()
看到每一個Bean初始化先後都會分別執行postProcessorBeforeInitiallization()方法與postProcessorAfterInitialization()方法,最後兩行出現緣由是,PostProcessorBean自己也是一個Bean。
接下來看另一個PostProcessor----BeanFactoryPostProcessor。
Spring容許在Bean建立以前,讀取Bean的元屬性,並根據本身的需求對元屬性進行改變,好比將Bean的scope從singleton改變爲prototype,最典型的應用應當是PropertyPlaceholderConfigurer,替換xml文件中的佔位符,替換爲properties文件中相應的key對應的value,這將會在下篇文章中專門講解PropertyPlaceholderConfigurer的做用及其原理。
BeanFactoryPostProcessor就能夠幫助咱們實現上述的功能,下面來看一下BeanFactoryPostProcessor的使用,定義一個BeanFactoryPostProcessor的實現類:
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public class FactoryPostProcessorBean implements BeanFactoryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory configurablelistablebeanfactory) throws BeansException { System.out.println("Enter FactoryPostProcessorBean.postProcessBeanFactory()\n"); } }
spring.xml裏面配置一下這個Bean,就不寫了,運行一下Spring容器,結果爲:
Enter FactoryPostProcessorBean.postProcessBeanFactory()
Enter ProcessorBean.postProcessBeforeInitialization()
Enter CommonBean.initMethod(), commonName = common0
Enter ProcessorBean.postProcessAfterInitialization()
Enter ProcessorBean.postProcessBeforeInitialization()
Enter CommonBean.initMethod(), commonName = common1
Enter ProcessorBean.postProcessAfterInitialization()
Enter ProcessorBean.postProcessBeforeInitialization()
Enter ProcessorBean.postProcessAfterInitialization()
從執行結果中能夠看出兩點:
一、BeanFactoryPostProcessor的執行優先級高於BeanPostProcessor
二、BeanFactoryPostProcessor的postProcessBeanFactory()方法只會執行一次
注意到postProcessBeanFactory方法是帶了參數ConfigurableListableBeanFactory的,這就和我以前說的可使用BeanFactoryPostProcessor來改變Bean的屬性相對應起來了。ConfigurableListableBeanFactory功能很是豐富,最基本的,它攜帶了每一個Bean的基本信息,好比我簡單寫一段代碼:
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public void postProcessBeanFactory(ConfigurableListableBeanFactory configurablelistablebeanfactory) throws BeansException { BeanDefinition beanDefinition = configurablelistablebeanfactory.getBeanDefinition("common0"); MutablePropertyValues beanProperty = beanDefinition.getPropertyValues(); System.out.println("scope before change:" + beanDefinition.getScope()); beanDefinition.setScope("singleton"); System.out.println("scope after change:" + beanDefinition.getScope()); System.out.println("beanProperty:" + beanProperty); }
看一下執行結果:
scope before change:
scope after change:singleton
beanProperty:PropertyValues: length=1; bean property 'commonName'
這樣就獲取了Bean的生命週期以及從新設置了Bean的生命週期。ConfigurableListableBeanFactory還有不少的功能,好比添加BeanPostProcessor,能夠本身去查看。
最後寫一個叫作InstantiationAwareBeanPostProcessor的PostProcessor。
InstantiationAwareBeanPostProcessor又表明了Spring的另一段生命週期:實例化。先區別一下Spring Bean的實例化和初始化兩個階段的主要做用:
一、實例化----實例化的過程是一個建立Bean的過程,即調用Bean的構造函數,單例的Bean放入單例池中
二、初始化----初始化的過程是一個賦值的過程,即調用Bean的setter,設置Bean的屬性
以前的BeanPostProcessor做用於過程(2)先後,如今的InstantiationAwareBeanPostProcessor則做用於過程(1)先後,看一下代碼,給前面的CommonBean加上構造函數:
public class CommonBean { public CommonBean() { System.out.println("Enter CommonBean's constructor"); } private String commonName; public void setCommonName(String commonName) { System.out.println("Enter CommonBean.setCommonName(), commonName = " + commonName); this.commonName = commonName; } public void initMethod() { System.out.println("Enter CommonBean.initMethod(), commonName = " + commonName); } }
實現InstantiationAwareBeanPostProcessor接口:
/** * @author 五月的倉頡 http://www.cnblogs.com/xrq730/p/5721366.html */ public class InstantiationAwareBeanPostProcessorBean implements InstantiationAwareBeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Enter InstantiationAwareBeanPostProcessorBean.postProcessAfterInitialization()"); return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("Enter InstantiationAwareBeanPostProcessorBean.postProcessBeforeInitialization()"); return bean; } public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { System.out.println("Enter InstantiationAwareBeanPostProcessorBean.postProcessAfterInstantiation()"); return true; } public Object postProcessBeforeInstantiation(Class<?> bean, String beanName) throws BeansException { System.out.println("Enter InstantiationAwareBeanPostProcessorBean.postProcessBeforeInstantiation()"); return null; } public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pd, Object bean, String beanName) throws BeansException { return pvs; } }
配置一下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-4.1.xsd"> <bean id="common" class="org.xrq.bean.common.CommonBean"> <property name="commonName" value="common"/> </bean> <bean class="org.xrq.bean.processor.InstantiationAwareBeanPostProcessorBean" /> </beans>
啓動容器,觀察一下運行結果爲:
Enter InstantiationAwareBeanPostProcessorBean.postProcessBeforeInstantiation()
Enter CommonBean's constructor
Enter InstantiationAwareBeanPostProcessorBean.postProcessAfterInstantiation()
Enter CommonBean.setCommonName(), commonName = common
Enter InstantiationAwareBeanPostProcessorBean.postProcessBeforeInitialization()
Enter InstantiationAwareBeanPostProcessorBean.postProcessAfterInitialization()
Enter InstantiationAwareBeanPostProcessorBean.postProcessAfterInstantiation()
Enter InstantiationAwareBeanPostProcessorBean.postProcessBeforeInitialization()
Enter InstantiationAwareBeanPostProcessorBean.postProcessAfterInitialization()
最後三行的運行結果不去關注,看到很明顯的,InstantiationAwareBeanPostProcessor做用的是Bean實例化先後,即:
一、Bean構造出來以前調用postProcessBeforeInstantiation()方法
二、Bean構造出來以後調用postProcessAfterInstantiation()方法
不過一般來說,咱們不會直接實現InstantiationAwareBeanPostProcessor接口,而是會採用繼承InstantiationAwareBeanPostProcessorAdapter這個抽象類的方式來使用。
若是隻會寫個Bean,配置在xml文件裏面,注入一下,那是最最基礎的Spring開發者。一箇中級、高級的Spring開發者,必然會對Spring中的多個擴展點有所瞭解,並利用這些擴展點更好地爲項目服務,使得整個代碼結構更加地優雅,而且可讀性、可維護性更好。 拋磚引玉,本文只是簡單地介紹一些經常使用的Spring Bean擴展接口以及它們的簡單用法,更深刻的或者它們一些合適的使用場景,還須要留待網友朋友們本身去探索。