本篇文章主要介紹了一些常用的Spring Bean擴充介面以及它們的簡單用法,具有很好的參考價值。下面跟著小編一起來看下吧
前言
Spring是一款非常強大的框架,可以說是幾乎所有的企業級Java專案都使用了Spring ,而Bean又是Spring框架的核心。
Spring框架運用了非常多的設計模式,從整體上看,它的設計嚴格遵循了OCP----開閉原則,即:
# 1、保證對修改關閉,即外部無法修改Spring整個運作的流程
2、提供對擴展開放,即可以透過繼承、實現Spring提供的眾多抽象類與介面來改變類別載入的行為
開卷有益,閱讀Spring原始碼(無需每個類別都看得很細,大體流程能梳理出來即可)對於個人水平的提升是幫助非常大的,同時也能在工作中即使發現和解決一些不常見的Spring問題。
不過,本文的目的不是整理Spring的流程,而是透過介紹一些常用的Spring Bean工具類,來讓我們可以更好地使用Spring提供給開發者的多種特性,下面讓我們開始吧。
InitialingBean和DisposableBean
#InitialingBean是一個接口,提供了一個唯一的方法afterPropertiesSet()。
DisposableBean也是一個接口,提供了一個唯一的方法destory()。
這兩個介面是一組的,功能類似,因此放在一起:前者顧名思義在Bean屬性都設定完畢後呼叫afterPropertiesSet()方法做一些初始化的工作,後者在Bean生命週期結束前呼叫destory()方法做一些收尾工作。下面看一下例子,為了能明確地知道afterPropertiesSet()方法的呼叫時機,加上一個屬性,給屬性set方法,在set方法中列印一些內容:
/** * @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:
<?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的屬性都被設定完畢之後,才會呼叫。
關於這兩個接口,我總結幾點:
1、InitializingBean接口、Disposable接口可以和init-method、destory-method配合使用,接口執行順序優先於配置
2、InitializingBean介面、Disposable介面底層使用類型強轉.方法名()進行直接方法調用,init-method、destory-method底層使用反射,前者和Spring耦合程度更高但效率高,後者解除了和Spring之間的耦合但是效率低,使用哪個看個人喜好
3、afterPropertiesSet ()方法是在Bean的屬性設定之後才會進行調用,某個Bean的afterPropertiesSet()方法執行完畢才會執行下一個Bean的afterPropertiesSet()方法,因此不建議在afterPropertiesSet()方法中寫入處理時間太長的方法
BeanNameAware、ApplicationContextAware和BeanFactoryAware
這三個介面放在一起寫,因為它們是一組的,作用相似。
"Aware"的意思是"感知到的",那麼這三個介面的意思也不難理解:
1、實作BeanNameAware介面的Bean,在Bean載入的過程中可以取得到該Bean的id
2、實作ApplicationContextAware介面的Bean,在Bean載入的過程中可以取得到Spring的ApplicationContext,這個尤其重要,ApplicationContext是Spring應用上下文,從ApplicationContext中可以取得包含任意的Bean在內的大量Spring容器內容和資訊
3、實作BeanFactoryAware介面的Bean,在Bean載入的過程中可以取得到載入該Bean的BeanFactory
#看範例:
/** * @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 hierarchy
關於這三個介面以及上面的列印訊息,總結幾點:
1、如果你的BeanName、ApplicationContext、BeanFactory有用,那麼就自己定義一個變數將它們儲存下來,如果沒用,那麼只需要實作setXXX()方法,用一下Spring注入進來的參數即可
2、如果Bean同時也實作了InitializingBean,容器會保證BeanName、ApplicationContext和BeanFactory在呼叫afterPropertiesSet()方法被注入
FactoryBean
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的几个方法:
1、getObject()方法是最重要的,控制Bean的实例化过程
2、getObjectType()方法获取接口返回的实例的class
3、isSingleton()方法获取该Bean是否为一个单例的Bean
像我这段代码的功能就是传入一个String类型的参数,可以动态控制生成出来的是接口的哪种子类。有了FactoryBean,同样的我们也可以灵活地操控Bean的生成。
BeanPostProcessor
之前的InitializingBean、DisposableBean、FactoryBean包括init-method和destory-method,针对的都是某个Bean控制其初始化的操作,而似乎没有一种办法可以针对每个Bean的生成前后做一些逻辑操作,PostProcessor则帮助我们做到了这一点,先看一个简单的BeanPostProcessor。
网上有一张图画了Bean生命周期的过程,画得挺好,原图出处:
BeanPostProcess接口有两个方法,都可以见名知意:
1、postProcessBeforeInitialization:在初始化Bean之前
2、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。
BeanFactoryPostProcessor
接下来看另外一个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()
从执行结果中可以看出两点:
1、BeanFactoryPostProcessor的执行优先级高于BeanPostProcessor
2、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
最后写一个叫做InstantiationAwareBeanPostProcessor的PostProcessor。
InstantiationAwareBeanPostProcessor又代表了Spring的另外一段生命周期:实例化。先区别一下Spring Bean的实例化和初始化两个阶段的主要作用:
1、实例化----实例化的过程是一个创建Bean的过程,即调用Bean的构造函数,单例的Bean放入单例池中
2、初始化----初始化的过程是一个赋值的过程,即调用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实例化前后,即:
1、Bean构造出来之前调用postProcessBeforeInstantiation()方法
2、Bean构造出来之后调用postProcessAfterInstantiation()方法
不过通常来讲,我们不会直接实现InstantiationAwareBeanPostProcessor接口,而是会采用继承InstantiationAwareBeanPostProcessorAdapter这个抽象类的方式来使用。
后记
如果只会写个Bean,配置在xml文件里面,注入一下,那是最最基础的Spring开发者。一个中级、高级的Spring开发者,必然会对Spring中的多个扩展点有所了解,并利用这些扩展点更好地为项目服务,使得整个代码结构更加地优雅,并且可读性、可维护性更好。
【相关推荐】
1. Java免费视频教程
2. JAVA教程手册
3. 全面解析Java注解
以上是關於Spring Bean擴充介面的方法介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SublimeText3漢化版
中文版,非常好用

WebStorm Mac版
好用的JavaScript開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器