接着上一篇文章《如果你不知道spring中的这些初始化方法,你就out了》,spring有三种初始化方式:
1.设置init-method方法
2.现实InitializingBean接口重写afterPropertiesSet方法
3.在方法上使用PostConstruct注解
这三种初始化方法的执行顺序是:PostConstruct > InitializingBean > init-method
那么问题来了,为什么是这样执行顺序?
我们一起看一下spring的源码
先看一下AbstractAutowireCapableBeanFactory类的doCreateBean方法,我们猜想它是处理PostConstruct注解的,不信我们一起看看。
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
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()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
图1
然后看一下applyBeanPostProcessorsAfterInitialization方法,
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
图2
查看postProcessBeforeInitialization时发现了很多类都重写了该方法,到底要进入哪个类的方法呢?
图3
我们重点看InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization方法,它先查找或创建LifecycleMetadata。
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, "Failed to invoke init method", ex);
}
return bean;
}
图4
再看看findLifecycleMetadata方法,先从缓存中查一下有没有LifecycleMetadata对象,如果有就直接返回;如果没有就创建,然后放到缓存中。当然这里用了使用了双重检测锁防止并发的情况判断有问题。
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(clazz);
}
// Quick check on the concurrent map first, with minimal locking.
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
metadata = buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
}
return metadata;
}
图5
接下来重点看一下buildLifecycleMetadata方法
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
final boolean debug = logger.isDebugEnabled();
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (debug) {
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (debug) {
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}
图6
注意这里关键代码
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (debug) {
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
图7
我们可以看到程序这里判断如果有initAnnotationType类型的注解,我们就创建LifecycleElement对象,放到一个currInitMethods集合中,然后将currInitMethods集合放到 initMethods集合中,最后创建一个包含了initMethods集合的LifecycleMetadata对象返回。
看到这里不禁想问initAnnotationType是什么鬼?
图8
图9
这里可以看到initAnnotationType是InitDestroyAnnotationBeanPostProcessor类的一个成员变量,通过调用setInitAnnotationType方法赋值的。那么setInitAnnotationType是在哪来调用的?
可以追溯到CommonAnnotationBeanPostProcessor的构造函数,
图10
惊喜,原来initAnnotationType就是PostConstruct。
以上代码的意思,无非是通过反射查询方法上面如果有PostConstruct就将该方法收集起来放到LifecycleMetadata对象中。
接下来,我们再回到图4的地方,会执行这一行代码metadata.invokeInitMethods(bean, beanName);
public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
Collection<LifecycleElement> initMethodsToIterate =
(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (LifecycleElement element : initMethodsToIterate) {
if (debug) {
logger.debug("Invoking init method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
图11
会循环收集到的打了PostConstruct注解的方法,
public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}
图12
直接通过反射调用该方法,即可完成初始化的功能。
至此,PostConstruct注解已经讲解完毕。
接下来,我们回头看一下图1 中的invokeInitMethods(beanName, wrappedBean, mbd);方法,
protected void invokeInitMethods(String beanName, final Object bean, @Nullable 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((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
图13
这段代码的意思是,先判断如果当前bean实现了InitializingBean接口,就调用afterPropertiesSet方法。
哈哈哈,这不就是我们要找的InitializingBean接口的afterPropertiesSet方法吗?
最后,再看一下下面的代码
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
图14
获取initMethod方法名称,判断方法名称如果不为空,并且没有继承InitializingBean接口并且InitializingBean不是“afterPropertiesSet”,则调用invokeCustomInitMethod方法,
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
String initMethodName = mbd.getInitMethodName();
Assert.state(initMethodName != null, "No init method set");
final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(bean.getClass(), initMethodName) :
ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
if (initMethod == null) {
if (mbd.isEnforceInitMethod()) {
throw new BeanDefinitionValidationException("Could not find an init method named '" +
initMethodName + "' on bean with name '" + beanName + "'");
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No default init method named '" + initMethodName +
"' found on bean with name '" + beanName + "'");
}
// Ignore non-existent default lifecycle methods.
return;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(initMethod);
return null;
});
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
initMethod.invoke(bean), getAccessControlContext());
}
catch (PrivilegedActionException pae) {
InvocationTargetException ex = (InvocationTargetException) pae.getException();
throw ex.getTargetException();
}
}
else {
try {
ReflectionUtils.makeAccessible(initMethod);
initMethod.invoke(bean);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
图15
通过initMethod方法名称反射获取method对象,如果method对象为空,则直接返回,如果不为空则调用initMethod.invoke(bean);
good,这样就可以调用到init-method指定的具体方法了。
好了,好了,废话不多说了,
通过源代码可以清楚的看到spring初始化方法的调用过程,在applyBeanPostProcessorsBeforeInitialization方法中先调用 打了PostConstruct注解的方法,然后 在invokeInitMethods方法中,先调用 实现了InitializingBean接口的afterPropertiesSet方法,紧接着调用 initMethod对应的具体方法。
所以初始化顺序是:
PostConstruct > InitializingBean > init-method
如果你是一个好奇的孩纸,可能会问:CommonAnnotationBeanPostProcessor对象是在哪来初始化的?谜底将会在下一篇文章中揭晓。