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