【课程免费分享】2-如何在Bean初始化回调前后进行自定义操作

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运行后可得到如下

enter image description here

是不是非常直观,从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一样的效果,那么这里来看下调用链关系:
enter image description here

从上图可以看出,@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,通过这个接口可以进行更多的业务逻辑操作,至于如何取舍那么就需要看项目的实际情况了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章