Spring 中的 initializeBean 方法小总结

157 阅读7分钟

Spring 中的 initializeBean 方法小总结

背景

spring-beans 模块中的 AbstractAutowireCapableBeanFactory.java 文件里有 initializeBean 方法,这个方法会负责 beaninitialization(初始化),本文会对其主要逻辑进行总结。

正文

该方法的代码复制如下 ⬇️

	/**
	 * Initialize the given bean instance, applying factory callbacks
	 * as well as init methods and bean post processors.
	 * <p>Called from {@link #createBean} for traditionally defined beans,
	 * and from {@link #initializeBean} for existing bean instances.
	 * @param beanName the bean name in the factory (for debugging purposes)
	 * @param bean the new bean instance we may need to initialize
	 * @param mbd the bean definition that the bean was created with
	 * (can also be {@code null}, if given an existing bean instance)
	 * @return the initialized bean instance (potentially wrapped)
	 * @see BeanNameAware
	 * @see BeanClassLoaderAware
	 * @see BeanFactoryAware
	 * @see #applyBeanPostProcessorsBeforeInitialization
	 * @see #invokeInitMethods
	 * @see #applyBeanPostProcessorsAfterInitialization
	 */
	@SuppressWarnings("deprecation")
	protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		// Skip initialization of a NullBean
		if (bean.getClass() == NullBean.class) {
			return bean;
		}

		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, ex.getMessage(), ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

其中的主要逻辑共有 4 步,我把 4 个步骤的位置在下图中标出来了 ⬇️ image.png

从调用的方法的名称来看,除了第 1 步之外,其余的 3 步都和 init/initialization(初始化)有关,不妨就把第 2/3/4 步分别简称为 "前"/"中"/"后",而第一步则可以简称为"知"。于是可以把 4 个步骤概括如下 ⬇️

主要步骤做了什么核心代码
Step 1: 处理 3BeanXXXAware 接口invokeAwareMethods(beanName, bean);
Step 2: Step 3 之前 调用各个 BeanPostProcessorpostProcessBeforeInitialization 方法wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
Step 3: 处理 InitializingBean 接口以及用户指定的 init methodinvokeInitMethods(beanName, wrappedBean, mbd);
Step 4: Step 3 之后 调用各个 BeanPostProcessorpostProcessAfterInitialization 方法wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

Step 1:

invokeAwareMethods(String, Object) 方法的代码复制如下 ⬇️

	private void invokeAwareMethods(String beanName, Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware beanNameAware) {
				beanNameAware.setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					beanClassLoaderAware.setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware beanFactoryAware) {
				beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

如果当前的 bean implement 了以下 3BeanXXXAware 接口中的任意一个,在 invokeAwareMethods(String, Object) 方法中就会进行相应处理。

BeanXXXAware 接口接口中定义了什么方法例子
BeanNameAwareimage.png假设一个 bean BB 需要感知自身的 beanName,我们可以让 BB implement BeanNameAware 接口,那么在 invokeAwareMethods(String, Object) 方法中就会用 BB 来调用 setBeanName(String) 方法,从而将对应的 beanName 填进去
BeanClassLoaderAwareimage.png
BeanFactoryAwareimage.png假设一个 bean BB 需要感知 BeanFactory,我们可以让 BB implement BeanFactoryAware 接口,那么在 invokeAwareMethods(String, Object) 方法中就会用 BB 来调用 setBeanFactory(BeanFactory) 方法,从而将 BeanFactory 填进去

简要的类图如下 ⬇️(类图中把方法声明里所抛的异常省略了)

classDiagram
class Aware
<<interface>> Aware
Aware <|-- BeanNameAware
<<interface>> BeanNameAware

Aware <|-- BeanClassLoaderAware

<<interface>> BeanClassLoaderAware

Aware <|-- BeanFactoryAware
<<interface>> BeanFactoryAware

BeanNameAware: void setBeanName(String name)
BeanClassLoaderAware: void setBeanClassLoader(ClassLoader classLoader)
BeanFactoryAware: void setBeanFactory(BeanFactory beanFactory)

由于上方的类图中的接口所在包的包名普遍很长,我把这些接口的名称与对应的 fully qualified class name 的关系列在下表里了 ⬇️

接口名fully qualified class name
Awareorg.springframework.beans.factory.Aware
BeanNameAwareorg.springframework.beans.factory.BeanNameAware
BeanClassLoaderAwareorg.springframework.beans.factory.BeanClassLoaderAware
BeanFactoryAwareorg.springframework.beans.factory.BeanFactoryAware

Step 2:

applyBeanPostProcessorsBeforeInitialization(Object, String) 方法的代码复制如下 ⬇️

	@Deprecated(since = "6.1")
	@Override
	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;
	}

这里的逻辑是遍历所有的 BeanPostProcessor,依次调用它们的 postProcessBeforeInitialization 方法(如果 postProcessBeforeInitialization 方法的返回值为 null,则提前结束)。

应用场景 1: @PostConstruct 的处理

我们平时用的 @PostConstruct 注解,它对应的 BeanPostProcessorCommonAnnotationBeanPostProcessor。而对 @PostConstruct 的支持,就体现在 Step 2 里(可以在它的基类 InitDestroyAnnotationBeanPostProcessorpostProcessBeforeInitialization(Object bean, String beanName) 方法 打断点进行验证,这里就不展开说了)。

CommonAnnotationBeanPostProcessor 相关的简要类图如下 ⬇️ (请注意,图中略去了所有的泛型以及方法声明中的所有异常,也略去了一些与本文关系不大的类/接口/方法)

classDiagram
class PriorityOrdered
class Ordered
class InstantiationAwareBeanPostProcessor
class BeanPostProcessor
class DestructionAwareBeanPostProcessor
class MergedBeanDefinitionPostProcessor
<<interface>> PriorityOrdered
<<interface>> Ordered
<<interface>> InstantiationAwareBeanPostProcessor
<<interface>> BeanPostProcessor
<<interface>> DestructionAwareBeanPostProcessor
<<interface>> MergedBeanDefinitionPostProcessor
Ordered <|-- PriorityOrdered
DestructionAwareBeanPostProcessor <|.. InitDestroyAnnotationBeanPostProcessor
MergedBeanDefinitionPostProcessor <|.. InitDestroyAnnotationBeanPostProcessor
InstantiationAwareBeanPostProcessor <|.. CommonAnnotationBeanPostProcessor
PriorityOrdered <|.. InitDestroyAnnotationBeanPostProcessor
InitDestroyAnnotationBeanPostProcessor <|-- CommonAnnotationBeanPostProcessor
Ordered: getOrder()
BeanPostProcessor <|-- InstantiationAwareBeanPostProcessor
BeanPostProcessor <|-- DestructionAwareBeanPostProcessor
BeanPostProcessor <|-- MergedBeanDefinitionPostProcessor
InstantiationAwareBeanPostProcessor: postProcessBeforeInstantiation(Class, String)
InstantiationAwareBeanPostProcessor: postProcessAfterInstantiation(Object, String)
InstantiationAwareBeanPostProcessor: postProcessProperties(PropertyValues, Object, String)

BeanPostProcessor: postProcessBeforeInitialization(Object, String)
BeanPostProcessor: postProcessAfterInitialization(Object, String) 
DestructionAwareBeanPostProcessor: postProcessBeforeDestruction(Object, String)
DestructionAwareBeanPostProcessor: boolean requiresDestruction(Object)

MergedBeanDefinitionPostProcessor: postProcessMergedBeanDefinition(RootBeanDefinition, Class, String)

MergedBeanDefinitionPostProcessor: resetBeanDefinition(String)

由于上方的类图中的类/接口所在包的包名普遍很长,我把这些类/接口的名称与对应的 fully qualified class name 的关系列在下表里了 ⬇️

类名/接口名fully qualified class name
CommonAnnotationBeanPostProcessororg.springframework.context.annotation.CommonAnnotationBeanPostProcessor
InitDestroyAnnotationBeanPostProcessororg.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
PriorityOrderedorg.springframework.core.PriorityOrdered
Orderedorg.springframework.core.Ordered
InstantiationAwareBeanPostProcessororg.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
BeanPostProcessororg.springframework.beans.factory.config.BeanPostProcessor
DestructionAwareBeanPostProcessororg.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
MergedBeanDefinitionPostProcessororg.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor
应用场景 2: ApplicationContextAwareProcessor

AbstractApplicationContext.java 里的 prepareBeanFactory(...) 方法中,会添加 ApplicationContextAwareProcessor 这个 BeanPostProcessor ⬇️

image.png

ApplicationContextAwareProcessor 里,可以找到如下的代码 ⬇️

	@Override
	public @Nullable Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof Aware) {
			invokeAwareInterfaces(bean);
		}
		return bean;
	}

	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof EnvironmentAware environmentAware) {
			environmentAware.setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware embeddedValueResolverAware) {
			embeddedValueResolverAware.setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware resourceLoaderAware) {
			resourceLoaderAware.setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware applicationEventPublisherAware) {
			applicationEventPublisherAware.setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware messageSourceAware) {
			messageSourceAware.setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationStartupAware applicationStartupAware) {
			applicationStartupAware.setApplicationStartup(this.applicationContext.getApplicationStartup());
		}
		if (bean instanceof ApplicationContextAware applicationContextAware) {
			applicationContextAware.setApplicationContext(this.applicationContext);
		}
	}

上面的逻辑比较直观,如果当前 bean BB implement 以下 7XXXAware 接口中的任何一个,那么这个接口上定义的 setXXX(...) 方法会被调用。

  • EnvironmentAware
  • EmbeddedValueResolverAware
  • ResourceLoaderAware
  • ApplicationEventPublisherAware
  • MessageSourceAware
  • ApplicationStartupAware
  • ApplicationContextAware

我画了简单的类图来展示这 7XXXAware 接口和 Aware 接口之间的关系 ⬇️

classDiagram
class Aware
class EnvironmentAware
class EmbeddedValueResolverAware
class ResourceLoaderAware
class ApplicationEventPublisherAware
class MessageSourceAware
class ApplicationStartupAware
class ApplicationContextAware
<<interface>> Aware
<<interface>> EnvironmentAware
<<interface>> EmbeddedValueResolverAware
<<interface>> ResourceLoaderAware
<<interface>> ApplicationEventPublisherAware
<<interface>> MessageSourceAware
<<interface>> ApplicationStartupAware
<<interface>> ApplicationContextAware
Aware <|-- EnvironmentAware
Aware <|-- EmbeddedValueResolverAware
Aware <|-- ResourceLoaderAware
Aware <|-- ApplicationEventPublisherAware
Aware <|-- MessageSourceAware
Aware <|-- ApplicationStartupAware
Aware <|-- ApplicationContextAware

EnvironmentAware: setEnvironment(Environment)
EmbeddedValueResolverAware: setEmbeddedValueResolver(StringValueResolver)
ResourceLoaderAware: setResourceLoader(ResourceLoader)
ApplicationEventPublisherAware: setApplicationEventPublisher(ApplicationEventPublisher)
MessageSourceAware: setMessageSource(MessageSource)
ApplicationStartupAware: setApplicationStartup(ApplicationStartup)
ApplicationContextAware: setApplicationContext(ApplicationContext)

由于上方的类图中的接口所在包的包名普遍很长,我把这些接口的名称与对应的 fully qualified class name 的关系列在下表里了 ⬇️

接口名fully qualified class name
Awareorg.springframework.beans.factory.Aware
EnvironmentAwareorg.springframework.context.EnvironmentAware
EmbeddedValueResolverAwareorg.springframework.context.EmbeddedValueResolverAware
ResourceLoaderAwareorg.springframework.context.ResourceLoaderAware
ApplicationEventPublisherAwareorg.springframework.context.ApplicationEventPublisherAware
MessageSourceAwareorg.springframework.context.MessageSourceAware
ApplicationStartupAwareorg.springframework.context.ApplicationStartupAware
ApplicationContextAwareorg.springframework.context.ApplicationContextAware

Step 3:

invokeInitMethods(String, Object, RootBeanDefinition) 方法的代码复制如下 ⬇️

	/**
	 * Give a bean a chance to initialize itself after all its properties are set,
	 * and a chance to know about its owning bean factory (this object).
	 * <p>This means checking whether the bean implements {@link InitializingBean}
	 * or defines any custom init methods, and invoking the necessary callback(s)
	 * if it does.
	 * @param beanName the bean name in the factory (for debugging purposes)
	 * @param bean the new bean instance we may need to initialize
	 * @param mbd the merged bean definition that the bean was created with
	 * (can also be {@code null}, if given an existing bean instance)
	 * @throws Throwable if thrown by init methods or by the invocation process
	 * @see #invokeCustomInitMethod
	 */
	protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			((InitializingBean) bean).afterPropertiesSet();
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String[] initMethodNames = mbd.getInitMethodNames();
			if (initMethodNames != null) {
				for (String initMethodName : initMethodNames) {
					if (StringUtils.hasLength(initMethodName) &&
							!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
							!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
						invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
					}
				}
			}
		}
	}

这个方法会处理以下 2 种情况

  • 如果当前 bean BB implementInitializingBean 接口,则尝试通过 BB 来调用 InitializingBean 接口中的 afterPropertiesSet() 方法
  • 如果当前 bean BB 指定了 init method(例如可以在 xml 文件里指定 init method),则尝试通过 BB 来调用这些 init method

Step 4:

applyBeanPostProcessorsAfterInitialization(Object, String) 方法的代码复制如下 ⬇️

	@Deprecated(since = "6.1")
	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

这一步和 Step 2 的代码类似,其逻辑是遍历所有的 BeanPostProcessor,依次调用它们的 postProcessAfterInitialization 方法 (如果 postProcessAfterInitialization 方法的返回值为 null,则提前结束)。

验证 4 个步骤

本文已经描述了 initializeBean 方法的 4 个步骤的主要逻辑。但是纸上得来终觉浅,我们还是写点代码验证一下。搭建一个完整的项目需要写不少代码,相信大家都有这方面的经验了,我只把核心的 java 代码以及 xml 文件的内容复制出来。

java 代码

请读者朋友在验证时,将以下 java 代码保存为 Main.java 并调整成合适的 package 语句。

package org.example.p0920;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

public class Main {
    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("simple-config.xml");
    }
}

class MyBPP implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行时机: Step 4(由 MyBPP 处理)");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}


class SimpleBean implements
        BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,
        EnvironmentAware, InitializingBean {

    @Override
    public void setBeanName(String name) {
        System.out.println("执行时机: Step 1 中的第 1 步(Step 1 里共有 3 步)");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("执行时机: Step 1 中的第 2 步(Step 1 里共有 3 步)");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("执行时机: Step 1 中的第 3 步(Step 1 里共有 3 步)");
    }


    @Override
    public void setEnvironment(Environment environment) {
        // AutowiredAnnotationBeanPostProcessor 中会处理 7 个 XXXAware 接口,这里仅以 EnvironmentAware 作为示例,
        // 另外 6 个就不赘述了
        System.out.println("执行时机: Step 2(由 AutowiredAnnotationBeanPostProcessor 处理)");
    }


    @Override
    public void afterPropertiesSet() {
        System.out.println("执行时机: Step 3 中的第 1 步(对 InitializingBean 接口的处理)");
    }

    public void specifiedInitMethod() {
        System.out.println("执行时机: Step 3 中的第 2 步(对 init method 的处理)");
    }
}
xml 文件

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"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"
>
    <bean id="simpleBean" class="org.example.p0920.SimpleBean" init-method="specifiedInitMethod"/>

    <bean id="bpp" class="org.example.p0920.MyBPP"/>

</beans>

请将以上代码保存为 simple-config.xml(用其他文件名保存也没问题,但是请注意调整对应的 java 代码,具体位置如下 ⬇️) image.png

另外,在您的电脑上,可以用其他的 package,但是请注意需要修改 xml 文件里对应的 package 名称(位置如下 ⬇️)

image.png

将代码准备好后,就可以运行 Main.java 中的 main(...) 方法了,相关的运行结果是 ⬇️

执行时机: Step 1 中的第 1 步(Step 1 里共有 3 步)
执行时机: Step 1 中的第 2 步(Step 1 里共有 3 步)
执行时机: Step 1 中的第 3 步(Step 1 里共有 3 步)
执行时机: Step 2(由 AutowiredAnnotationBeanPostProcessor 处理)
执行时机: Step 3 中的第 1 步(对 InitializingBean 接口的处理)
执行时机: Step 3 中的第 2 步(对 init method 的处理)
执行时机: Step 4(由 MyBPP 处理)

可见,initializeBean 方法的确是按照 ""/""/""/"" 4 个步骤来运转的。