前言
Spring是一款非常強大的框架,可以說是幾乎所有的企業(yè)級Java項目使用了Spring,而Bean又是Spring框架的核心。
Spring框架運用了非常多的設(shè)計模式,從整體上看,它的設(shè)計嚴格遵循了OCP----開閉原則,即:
1、保證對修改關(guān)閉,即外部無法修改Spring整個運作的流程
2、提供對擴展開放,即可以通過繼承、實現(xiàn)Spring提供的眾多抽象類與接口來改變類加載的行為
開卷有益,閱讀Spring源碼(無需每個類都看得很細,大體流程能梳理出來即可)對于個人水平的提升是幫助非常大的,同時也能在工作中即使發(fā)現(xiàn)和解決一些不常見的Spring問題。
不過,本文的目的不是整理Spring的流程,而是通過介紹一些常用的Spring Bean工具類,來讓我們可以更好地使用Spring提供給開發(fā)者的多種特性,下面讓我們開始吧。
InitialingBean和DisposableBean
InitialingBean是一個接口,提供了一個唯一的方法afterPropertiesSet()。
DisposableBean也是一個接口,提供了一個唯一的方法destory()。
這兩個接口是一組的,功能類似,因此放在一起:前者顧名思義在Bean屬性都設(shè)置完畢后調(diào)用afterPropertiesSet()方法做一些初始化的工作,后者在Bean生命周期結(jié)束前調(diào)用destory()方法做一些收尾工作。下面看一下例子,為了能明確地知道afterPropertiesSet()方法的調(diào)用時機,加上一個屬性,給屬性set方法,在set方法中打印一些內(nèi)容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
/** * @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:
1
2
3
4
5
6
7
8
9
10
|
<? 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執(zhí)行的結(jié)果為:
1
2
3
4
5
|
Enter LifecycleBean.setLifeCycleBeanName(), lifeCycleBeanName = lifeCycleBean Enter LifecycleBean.afterPropertiesSet() Enter LifecycleBean.beanStart() Enter LifecycleBean.destroy() Enter LifecycleBean.beanEnd() |
執(zhí)行結(jié)果和我們想的一樣,afterPropertiesSet()方法就如同它的名字所表示的那樣,是在Bean的屬性都被設(shè)置完畢之后,才會調(diào)用。
關(guān)于這兩個接口,我總結(jié)幾點:
1、InitializingBean接口、Disposable接口可以和init-method、destory-method配合使用,接口執(zhí)行順序優(yōu)先于配置
2、InitializingBean接口、Disposable接口底層使用類型強轉(zhuǎn).方法名()進行直接方法調(diào)用,init-method、destory-method底層使用反射,前者和Spring耦合程度更高但效率高,后者解除了和Spring之間的耦合但是效率低,使用哪個看個人喜好
3、afterPropertiesSet()方法是在Bean的屬性設(shè)置之后才會進行調(diào)用,某個Bean的afterPropertiesSet()方法執(zhí)行完畢才會執(zhí)行下一個Bean的afterPropertiesSet()方法,因此不建議在afterPropertiesSet()方法中寫處理時間太長的方法
BeanNameAware、ApplicationContextAware和BeanFactoryAware
這三個接口放在一起寫,是因為它們是一組的,作用相似。
"Aware"的意思是"感知到的",那么這三個接口的意思也不難理解:
1、實現(xiàn)BeanNameAware接口的Bean,在Bean加載的過程中可以獲取到該Bean的id
2、實現(xiàn)ApplicationContextAware接口的Bean,在Bean加載的過程中可以獲取到Spring的ApplicationContext,這個尤其重要,ApplicationContext是Spring應(yīng)用上下文,從ApplicationContext中可以獲取包括任意的Bean在內(nèi)的大量Spring容器內(nèi)容和信息
3、實現(xiàn)BeanFactoryAware接口的Bean,在Bean加載的過程中可以獲取到加載該Bean的BeanFactory
看一下例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/** * @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:
1
2
3
4
5
6
7
|
<? 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容器后的執(zhí)行結(jié)果為:
1
2
3
4
5
|
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 hierarchy |
關(guān)于這三個接口以及上面的打印信息,總結(jié)幾點:
1、如果你的BeanName、ApplicationContext、BeanFactory有用,那么就自己定義一個變量將它們保存下來,如果沒用,那么只需要實現(xiàn)setXXX()方法,用一下Spring注入進來的參數(shù)即可
2、如果Bean同時還實現(xiàn)了InitializingBean,容器會保證BeanName、ApplicationContext和BeanFactory在調(diào)用afterPropertiesSet()方法被注入
FactoryBean
FactoryBean在Spring中是非常有用的,使用Eclipse/MyEclipse的朋友可以對FactoryBean使用ctrl+t查看一下,F(xiàn)actoryBean這個接口在Spring容器中有大量的子實現(xiàn)。
傳統(tǒng)的Spring容器加載一個Bean的整個過程,都是由Spring控制的,換句話說,開發(fā)者除了設(shè)置Bean相關(guān)屬性之外,是沒有太多的自主權(quán)的。FactoryBean改變了這一點,開發(fā)者可以個性化地定制自己想要實例化出來的Bean,方法就是實現(xiàn)FactoryBean接口。
看一下代碼例子,為了講清楚FactoryBean,內(nèi)容相對多一些,首先定義一個接口Animal:
1
2
3
4
|
public interface Animal { public void move(); } |
定義兩個實現(xiàn)類Monkey和Tiger:
1
2
3
4
5
6
7
|
public class Monkey implements Animal { public void move() { System.out.println( "Monkey move!" ); } } |
1
2
3
4
5
6
7
|
public class Tiger implements Animal { public void move() { System.out.println( "Tiger move!" ); } } |
寫一個實現(xiàn)類,實現(xiàn)FactoryBean接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/** * @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:
1
2
3
4
5
6
7
8
9
10
|
<? 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的測試類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations = { "classpath*:spring.xml" , }) public class BaseTest { @Resource private Animal animal; @Test public void aa() { animal.move(); } } |
查看一下運行結(jié)果:
Tiger move!
看到最后得到的并不是FactoryBean本身,而是FactoryBean的泛型對象,這就是FactoryBean的作用。FactoryBean的幾個方法:
1、getObject()方法是最重要的,控制Bean的實例化過程
2、getObjectType()方法獲取接口返回的實例的class
3、isSingleton()方法獲取該Bean是否為一個單例的Bean
像我這段代碼的功能就是傳入一個String類型的參數(shù),可以動態(tài)控制生成出來的是接口的哪種子類。有了FactoryBean,同樣的我們也可以靈活地操控Bean的生成。
BeanPostProcessor
之前的InitializingBean、DisposableBean、FactoryBean包括init-method和destory-method,針對的都是某個Bean控制其初始化的操作,而似乎沒有一種辦法可以針對每個Bean的生成前后做一些邏輯操作,PostProcessor則幫助我們做到了這一點,先看一個簡單的BeanPostProcessor。
網(wǎng)上有一張圖畫了Bean生命周期的過程,畫得挺好,原圖出處:
BeanPostProcess接口有兩個方法,都可以見名知意:
1、postProcessBeforeInitialization:在初始化Bean之前
2、postProcessAfterInitialization:在初始化Bean之后
值得注意的是,這兩個方法是有返回值的,不要返回null,否則getBean的時候拿不到對象。
寫一段測試代碼,首先定義一個普通的Bean,為了后面能區(qū)分,給Bean加一個屬性:
1
2
3
4
5
6
7
8
9
10
11
12
|
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,實現(xiàn)BeanPostProcess接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/** * @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賦予不同的值以區(qū)分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<? 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容器, 初始化結(jié)果為:
1
2
3
4
5
6
7
8
9
|
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初始化前后都會分別執(zhí)行postProcessorBeforeInitiallization()方法與postProcessorAfterInitialization()方法,最后兩行出現(xiàn)原因是,PostProcessorBean本身也是一個Bean。
BeanFactoryPostProcessor
接下來看另外一個PostProcessor----BeanFactoryPostProcessor。
Spring允許在Bean創(chuàng)建之前,讀取Bean的元屬性,并根據(jù)自己的需求對元屬性進行改變,比如將Bean的scope從singleton改變?yōu)閜rototype,最典型的應(yīng)用應(yīng)當是PropertyPlaceholderConfigurer,替換xml文件中的占位符,替換為properties文件中相應(yīng)的key對應(yīng)的value,這將會在下篇文章中專門講解PropertyPlaceholderConfigurer的作用及其原理。
BeanFactoryPostProcessor就可以幫助我們實現(xiàn)上述的功能,下面來看一下BeanFactoryPostProcessor的使用,定義一個BeanFactoryPostProcessor的實現(xiàn)類:
1
2
3
4
5
6
7
8
9
10
11
|
/** * @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容器,結(jié)果為:
1
2
3
4
5
6
7
8
9
10
11
|
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() |
從執(zhí)行結(jié)果中可以看出兩點:
1、BeanFactoryPostProcessor的執(zhí)行優(yōu)先級高于BeanPostProcessor
2、BeanFactoryPostProcessor的postProcessBeanFactory()方法只會執(zhí)行一次
注意到postProcessBeanFactory方法是帶了參數(shù)ConfigurableListableBeanFactory的,這就和我之前說的可以使用BeanFactoryPostProcessor來改變Bean的屬性相對應(yīng)起來了。ConfigurableListableBeanFactory功能非常豐富,最基本的,它攜帶了每個Bean的基本信息,比如我簡單寫一段代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/** * @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); } |
看一下執(zhí)行結(jié)果:
1
2
3
|
scope before change: scope after change:singleton beanProperty:PropertyValues: length=1; bean property 'commonName' |
這樣就獲取了Bean的生命周期以及重新設(shè)置了Bean的生命周期。ConfigurableListableBeanFactory還有很多的功能,比如添加BeanPostProcessor,可以自己去查看。
InstantiationAwareBeanPostProcessor
最后寫一個叫做InstantiationAwareBeanPostProcessor的PostProcessor。
InstantiationAwareBeanPostProcessor又代表了Spring的另外一段生命周期:實例化。先區(qū)別一下Spring Bean的實例化和初始化兩個階段的主要作用:
1、實例化----實例化的過程是一個創(chuàng)建Bean的過程,即調(diào)用Bean的構(gòu)造函數(shù),單例的Bean放入單例池中
2、初始化----初始化的過程是一個賦值的過程,即調(diào)用Bean的setter,設(shè)置Bean的屬性
之前的BeanPostProcessor作用于過程(2)前后,現(xiàn)在的InstantiationAwareBeanPostProcessor則作用于過程(1)前后,看一下代碼,給前面的CommonBean加上構(gòu)造函數(shù):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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); } } |
實現(xiàn)InstantiationAwareBeanPostProcessor接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/** * @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:
1
2
3
4
5
6
7
8
9
10
11
12
|
<? 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 > |
啟動容器,觀察一下運行結(jié)果為:
1
2
3
4
5
6
7
8
9
|
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() |
最后三行的運行結(jié)果不去關(guān)注,看到很明顯的,InstantiationAwareBeanPostProcessor作用的是Bean實例化前后,即:
1、Bean構(gòu)造出來之前調(diào)用postProcessBeforeInstantiation()方法
2、Bean構(gòu)造出來之后調(diào)用postProcessAfterInstantiation()方法
不過通常來講,我們不會直接實現(xiàn)InstantiationAwareBeanPostProcessor接口,而是會采用繼承InstantiationAwareBeanPostProcessorAdapter這個抽象類的方式來使用。
后記
如果只會寫個Bean,配置在xml文件里面,注入一下,那是最最基礎(chǔ)的Spring開發(fā)者。一個中級、高級的Spring開發(fā)者,必然會對Spring中的多個擴展點有所了解,并利用這些擴展點更好地為項目服務(wù),使得整個代碼結(jié)構(gòu)更加地優(yōu)雅,并且可讀性、可維護性更好。
拋磚引玉,本文只是簡單地介紹一些常用的Spring Bean擴展接口以及它們的簡單用法,更深入的或者它們一些合適的使用場景,還需要留待網(wǎng)友朋友們自己去探索。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持服務(wù)器之家!
原文鏈接:http://www.cnblogs.com/xrq730/p/5721366.html