2、如何在Bean初始化回调前后进行自定义操作
在Spring环境中,如果需要在bean自动装配(属性都注入ok)完成后进行自定义操作,通常只需要实现接口InitializingBean,在afterPropertiesSet方法中执行操作即可。在这个接口回调时,bean中所有的属性已经注入完成了。比如在bean初始化完成后添加一段log:
@Component
public class MyBean implements InitializingBean {
private Logger log = LoggerFactory.getLogger(MyBean.class);
@Override
public void afterPropertiesSet() throws Exception {
log.info("=======>afterPropertiesSet");
}
}
项目启动后可以看到log输出
INFO com.cml.chat.lesson.lesson2.MyBean - =======>afterPropertiesSet
这样的实现方式适合单独的bean,如果有多个bean都具有一样的业务逻辑,那么抽象出一个共同类出来即可。所有的类都继承这个抽象类,但是这样的方式代码入侵大,不太适合用于框架类的项目和共通业务逻辑处理。那么如何更优雅的在bean初始化前后自定义进行处理呢?这时候BeanPostProcessor就派上用场了。
BeanPostProcessor
BeanPostProcessor接口提供了
- postProcessBeforeInitialization
在bean初始化回调前调用
- postProcessAfterInitialization
在bean初始化回调完成后进行调用,而且会在afterPropertiesSet方法回调之后。
两个方法,可以在bean初始化回调前后进行处理。而且使用也非常简单,只需要实现BeanPostProcessor接口就可以在bean初始化回调前后进行处理其他业务逻辑。这里做个简单的使用示例:
先定义好IMyBean接口,提供setCustomValue,getCustomValue两个方法,当所有IMyBean对象getCustomValue获取数据为空时,自动设置customValue为默认值"defaultValue";
public interface IMyBean {
void setCustomValue(String v);
String getCustomValue();
}
定义好MyBean实现IMyBean接口
@Component
public class MyBean implements IMyBean {
private String customValue;
public String getCustomValue() {
return customValue;
}
public void setCustomValue(String customValue) {
this.customValue = customValue;
}
}
添加MyBeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
private Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof IMyBean) {
log.info("=======>postProcessAfterInitialization");
IMyBean mybean = (IMyBean) bean;
if (mybean.getCustomValue() == null) {
mybean.setCustomValue("defaultValue");
}
}
return bean;
}
}
运行:
@SpringBootApplication()
public class Application {
private static Logger log=LoggerFactory.getLogger(Application.class);
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication(Application.class);
// 非web环境
springApplication.setWebEnvironment(false);
ConfigurableApplicationContext application = springApplication.run(args);
MyBean mybean = application.getBean(MyBean.class);
log.info("getCustomValue:"+mybean.getCustomValue());
}
}
log:
com.cml.chat.lesson.lesson2.Application - getCustomValue:defaultValue
当设置了customValue时,
String customValue = "myCustomValue";
输出:
com.cml.chat.lesson.lesson2.Application - getCustomValue:myCustomValue
这样简单的BeanPostProcessor就实现了,那么为什么只要实现BeanPostProcessor接口就可以了?Spring是如何识别BeanPostProcessor的呢?那么这时候该来剖析下BeanPostProcessor实现原理了。
BeanPostProcessor原理解析
在Spring中,所有的Bean都是通过BeanFactory进行管理的,SpringBoot中使用的是DefaultListableBeanFactory。可以从以下代码获取SpringBoot使用的BeanFactory
@SpringBootApplication()
public class Application {
private static Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication(Application.class);
// 非web环境
springApplication.setWebEnvironment(false);
ConfigurableApplicationContext application = springApplication.run(args);
log.info("beanFactory===>" + application.getBeanFactory().getClass());
application.close();
}
}
输出log:
com.cml.chat.lesson.lesson2.Application - beanFactory===>class org.springframework.beans.factory.support.DefaultListableBeanFactory
当获取bean时,都是通过调用BeanFactory.getBean方法获得的。在SpingBoot中BeanFactory默认使用的是DefaultListableBeanFactory。知道了BeanFactory就相当于找到了所有bean相关的入口。那么剩下的就该来剖析下BeanPostProcessor实现原理了。
首先先说下一个万能的原理查看方法:
只要在对应的调用地方添加断点,当断点进入后就可以看到整个方法调用链,这样就可以知道整个流程是如何运作了。就从上面的例子来说在MyBeanPostProcessor.postProcessBeforeInitialization方法中添加断点,debug运行后可得到如下
是不是非常直观,从Application调用入口,到断点MyBeanPostProcessor.postProcessBeforeInitialization中间的所有调用过程都展示出来了,那么源码阅读的话就可以按照这个流程逆向进行分析。
根据调用链可以进入AbstractAutowireCapableBeanFactory.initializeBean方法中
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
//略...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//这里调用了postProcessBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//这里调用了postProcessAfterInitialization
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
- applyBeanPostProcessorsBeforeInitialization
获取系统中所有的BeanPostProcessor对象,并调用其postProcessBeforeInitialization方法
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
- applyBeanPostProcessorsAfterInitialization
获取系统中所有的BeanPostProcessor对象,并调用其postProcessAfterInitialization方法
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
getBeanPostProcessors(),这里就更简单了,直接获取到BeanPostProcessor对象集合。
public List<BeanPostProcessor> getBeanPostProcessors() {
return this.beanPostProcessors;
}
那么是在什么时候将BeanPostProcessor对象都添加到集合中去的呢?
在Spring初始化完成后会回调ApplicationContext中的refresh方法,在AbstractApplicationContext.refresh()中完成了共通逻辑的实现。源码如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//略。。。
//从这里注册BeanPostProcessors
registerBeanPostProcessors(beanFactory);
//略。。。
}
}
registerBeanPostProcessors这里非常简单的调用了PostProcessorRegistrationDelegate,将BeanFactory作为参数传入,那么这里是不是就是可以猜想出是如何获取BeanPostProcessor的吧!
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
继续跟入到registerBeanPostProcessors,这里的第一句代码就很清楚的说明了获取的方式,从Bean工厂中获取到所有BeanPostProcessor实现类。
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);}
获取完所有实现类后,就是根据实现类上的顺序进行排序,然后将排序好的集合对象调用BeanFactory.addBeanPostProcessor注册到BeanFactory中。这样在AbstractAutowireCapableBeanFactory中就可以从它的父类AbstractBeanFactory中直接获取到BeanPostProcessor集合对象了,也就是上面调用的getBeanPostProcessors()方法。registerBeanPostProcessors方法实现的代码没有什么难度,这里就不多贴代码了。
以上代码从getBean入口到实际调用BeanPostProcessor接口的核心流程分析完毕了。知道 了BeanPostProcessor执行过程,那么InitializingBean的是何时回调的呢?
AbstractAutowireCapableBeanFactory.initializeBean方法中调用applyBeanPostProcessorsBeforeInitialization方法后,调用了invokeInitMethods方法。从方法的实现代码可以看出,如果bean实现了InitializingBean接口,就回调afterPropertiesSet方法。
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
这样BeanPostProcessor和InitializingBean的执行关系如下:
postProcessBeforeInitialization->afterPropertiesSet->postProcessAfterInitialization
在bean加载完成后回调中,还可以使用@PostConstruct实现。能够实现和
InitializingBean一样的效果,那么这里来看下调用链关系:
从上图可以看出,@PostConstruct是通过反射进行调用的。在InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization通过反射调用方法。源码如下:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Couldn't invoke init method", ex);
}
return bean;
}
这里可以看出,@PostConstruct其实也是通过BeanPostProcessor机制来实现的,这个可以说是BeanPostProcessor使用的一个非常好的例子。 其中LifecycleMetadata存储了添加了@PostConstruct注解的所有方法,然后通过反射循环调用所有的对应的方法。这里的源码就不继续深究了,有兴趣的可以自行查看。
总结
通过以上的讲解,这里可以得出@PostConstruct,BeanPostProcessor,InitializingBean之间的调用顺序:
BeanPostProcessor.postProcessBeforeInitialization->@PostConstruct->InitializingBean->BeanPostProcessor.postProcessAfterInitialization
既然@PostConstruct,BeanPostProcessor,InitializingBean 都可以实现在bean初始化完成后执行特定的操作,至于使用哪种还是看项目的使用习惯了,通常来说InitializingBean是使用最多的,@PostConstruct使用注解的方式来实现的话不够直观,对后期维护来说可能不太适合。BeanPostProcessor比较适合在框架类型或者面向特定接口使用。
这里重点还是BeanPostProcessor,通过这个接口可以进行更多的业务逻辑操作,至于如何取舍那么就需要看项目的实际情况了。