前言html
Spring是一款很是强大的框架,能够说是几乎全部的企业级Java项目使用了Spring,而Bean又是Spring框架的核心。spring
Spring框架运用了很是多的设计模式,从总体上看,它的设计严格遵循了OCP----开闭原则,即:设计模式
一、保证对修改关闭,即外部没法修改Spring整个运做的流程app
二、提供对扩展开放,便可以经过继承、实现Spring提供的众多抽象类与接口来改变类加载的行为框架
开卷有益,阅读Spring源码(无需每一个类都看得很细,大致流程能梳理出来便可)对于我的水平的提高是帮助很是大的,同时也能在工做中即便发现和解决一些不常见的Spring问题。函数
不过,本文的目的不是整理Spring的流程,而是经过介绍一些经常使用的Spring Bean工具类,来让咱们能够更好地使用Spring提供给开发者的多种特性,下面让咱们开始吧。工具
InitialingBean和DisposableBeanpost
InitialingBean是一个接口,提供了一个惟一的方法afterPropertiesSet()。测试
DisposableBean也是一个接口,提供了一个惟一的方法destory()。ui
这两个接口是一组的,功能相似,所以放在一块儿:前者顾名思义在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的属性都被设置完毕以后,才会调用。
关于这两个接口,我总结几点:
一、InitializingBean接口、Disposable接口能够和init-method、destory-method配合使用,接口执行顺序优先于配置
二、InitializingBean接口、Disposable接口底层使用类型强转.方法名()进行直接方法调用,init-method、destory-method底层使用反射,前者和Spring耦合程度更高但效率高,后者解除了和Spring之间的耦合可是效率低,使用哪一个看我的喜爱
三、afterPropertiesSet()方法是在Bean的属性设置以后才会进行调用,某个Bean的afterPropertiesSet()方法执行完毕才会执行下一个Bean的afterPropertiesSet()方法,所以不建议在afterPropertiesSet()方法中写处理时间太长的方法
BeanNameAware、ApplicationContextAware和BeanFactoryAware
这三个接口放在一块儿写,是由于它们是一组的,做用类似。
"Aware"的意思是"感知到的",那么这三个接口的意思也不难理解:
一、实现BeanNameAware接口的Bean,在Bean加载的过程当中能够获取到该Bean的id
二、实现ApplicationContextAware接口的Bean,在Bean加载的过程当中能够获取到Spring的ApplicationContext,这个尤为重要,ApplicationContext是Spring应用上下文,从ApplicationContext中能够获取包括任意的Bean在内的大量Spring容器内容和信息
三、实现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
关于这三个接口以及上面的打印信息,总结几点:
一、若是你的BeanName、ApplicationContext、BeanFactory有用,那么就本身定义一个变量将它们保存下来,若是没用,那么只须要实现setXXX()方法,用一下Spring注入进来的参数便可
二、若是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的几个方法:
一、getObject()方法是最重要的,控制Bean的实例化过程
二、getObjectType()方法获取接口返回的实例的class
三、isSingleton()方法获取该Bean是否为一个单例的Bean
像我这段代码的功能就是传入一个String类型的参数,能够动态控制生成出来的是接口的哪一种子类。有了FactoryBean,一样的咱们也能够灵活地操控Bean的生成。
BeanPostProcessor
以前的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。
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()
从执行结果中能够看出两点:
一、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
最后写一个叫作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扩展接口以及它们的简单用法,更深刻的或者它们一些合适的使用场景,还须要留待网友朋友们本身去探索。