前言:
准备好源码,保证看完就会,这是根本不用背诵的东西,我们发车了!
随便一张网图都会告诉你:
这么长怎么记??总结一下,主要无非四个阶段:
疑问1:直接就实例化一个bean了?spring这么神奇吗,他怎么知道哪些需要实例化?
其实,需要实例化哪些bean,也是我们使用者告诉spring的,主要有两种方式,xml配置和注解注入(主要是Autowire等),也就是说,spring会根据bean的定义信息(BeanDefinition),得到一份需要实例化的bean列表,然后才开始实例化。
疑问2:属性赋值和初始化,这两个步骤不都是给创建出来的bean设置相关的值嘛,为什么还要拆成两个步骤?
这里我就直接揭晓答案了,实例化阶段是spring采用反射机制实现的,非常复杂,并且spring也给我们预留了很多扩展点,单独提取成一个阶段,也是合适的。熟悉bean生命周期的源码的朋友可能会记得,在源码org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 这个方法中,明确写了两步:populateBean 和 initializeBean ,这里为了和源码保持一致,便于理解。粘贴一段简化的doCreateBean代码:
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 实例化阶段!
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性赋值阶段!
populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段!
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
那也就是说,我们流程变成了5个阶段:
然后,我们的源码分析也将会按照这个流程进行。
这个阶段我们需要记住一个接口:BeanDefinitionReader,这个接口从名字就能知道,这是用来获取bean定义信息的,并且通过loadBeanDefinitions方法来规范和定义该项职责,它有4个实现类:
写过spring MVC的小伙伴一定写过相应bean的配置文件xml,例如:
那么是谁负责解析这个bean的xml配置文件来创建bean的呢,就是上图的 XmlBeanDefinitionReader,这里有一个主要的方法:
// 只保留主要逻辑以后的代码:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
// 从resource指定的xml配置文件读入bean的定义信息
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 加载xml的document
Document doc = doLoadDocument(inputSource, resource);
// 加载解析完成之后,注册bean的定义信息,返回值是解析成功并注册的bean对象有几个
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
...
}
该方法执行完之后,会将bean的xml配置解析成 BeanDenifition 对象。
至此,第一步:“根据bean定义信息得到需要初始化的bean列表 ”已经揭开了面纱,剩下的就是一些小细节,小伙伴们可以自行阅读。
完成了第一步就得到了bean的定义信息,就可以创建bean了,怎么创建呢? new?,显然不是,spring是采用的反射来实例化对象。
也是从这一阶段开始,我们要记住一个非常重要的接口: BeanFactory,这个接口的实现类将会贯穿整个bean的生命周期,这也是几乎所有网上能搜到的帖子浓墨重彩描述的 bean 工厂。
先抛出一个问题,能不能根据第一步得到的BeanDefinition对象直接反射创建对象?
答案是不能,因为这里得到的BeanDefinition对象还有很多信息是不全的,比如:附属bean的配置信息、占位符没替换、自定义的逻辑没处理等等,所以,在实例化bean之前还有很多需要处理,这就是 BeanFactoryPostProcessor 接口的功能。
可能有人会说,你怎么知道的,我不能听你说什么就是什么啊,源码这里并没有相关的逻辑啊??
很好,回答这个问题,先跟着我的思路来:
我们的springboot项目启动方法:
public static void main(String[] args) {
SpringApplication.run(MyTestApplication.class, args);
}
这个方法一直追踪进去,会找到 org.springframework.context.support.AbstractApplicationContext#refresh 这个方法,
AbstractApplicationContext是Spring应用上下文中最重要的一个类,这个抽象类中提供了几乎ApplicationContext的所有操作。
而我所说的都是在这个类的核心方法 refresh 中实现的逻辑:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 注意看这里,得到了一个BeanFactory的对象,然后用该对象执行的各种逻辑
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
//这里执行了很多 process的逻辑
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
...
}
简单总结一下,项目启动需要加载bean,所以我们去跟踪源码,找到了AbstractApplicationContext.refresh 这个方法,这个方法在实例化 bean之前,执行了很多 processer的逻辑,完成了各种复杂配置、自定义配置的处理,至此,回答了上述问题。
我们的进度走到了:
其实,这也回答了一个经典的面试题:AbstractApplicationContext 和 BeanFactory 有什么区别和联系。
然后,才是实例化阶段。源码到了 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 的createBeanInstance逻辑,一直跟进去会找到 org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate方法,基本就能看出来spring采用的是反射实例化bean:
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
至此,我们完成了第二步实例化,进度来到了:
先粘贴一下 doCreateBean 主要逻辑的简化代码:
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 实例化阶段!
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性赋值阶段!
populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段!
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
我们已经走到了 populateBean 阶段,这里的逻辑比较简单,就是给属性set相关的值即可,这些值在第一步的时候已经转换成BeanDefinition对象了,直接取值即可,逻辑相对简单,小伙伴可以自行阅读相关代码。
然后,按照惯例,我们走到了 initializeBean 阶段,有人会问,这不是下一阶段干的事情吗,怎么还要放在属性赋值阶段说呢?
initializeBean 中调用了一个非常重要方法 invokeAwareMethods ,这个方法检查了 Aware 相关接口的实现和配置,执行了这些逻辑之后,才调用了invokeInitMethods 执行初始化逻辑,所以我们这里将 invokeAwareMethods 放在属性赋值阶段讲。
看一下invokeAwareMethods的源代码:
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
非常简单,就是将bean的几个属性变量设置了一下,然后再使用的时候可以直接用。
至此,我们完成了bean的属性赋值阶段,进度来到了:
思路回到bean的属性赋值之后,然后上一张图:
刚才在上一阶段讲了绿色方框的 invokeAwareMethods 方法,这里在真正的初始化方法 invokeInitMethods 之前、之后,各有一个步骤(红色方框圈出来的部分),这里就是初始化的前置和后置处理器。
这部分代码也相对比较简单,因为大部分的bean创建之前和之后都是不需要做额外操作的,需要魔改的都是spring框架留给开发者的扩展接口,想干什么操作,实现相关的接口,然后实现相关的方法,就能魔改bean了。
至此,我们完成了bean的初始化,进度来到了:
销毁部分99%的场景都是由spring框架完成的,只有当容器关闭的时候,就会执行bean的销毁逻辑,我们使用者并不需要过多地关注销毁,就好比写Java代码的时候,我们不用太关注JVM的垃圾回收一样。
相信拿着源码和我一起阅读到这里的小伙伴一定熟悉了整个流程,最后,来一张全图: