【spring容器启动】之bean的实例化和初始化(文末附:spring循环依赖原理)
本次我们通过源码介绍ApplicationContext容器初始化流程,主要介绍容器内bean的实例化和初始化过程。ApplicationContext是Spring推出的先进Ioc容器,它继承了旧版本Ioc容器BeanFactory,并进一步扩展了容器的功能,增加了bean的自动识别、自动初始化等功能,同时引入了BeanFactoryPostProcessor、BeanPostProcessor等逻辑处理组件,目前我们使用的spring项目大部分都是基于ApplicationContext容器的。ApplicationContext容器在启动阶段便会调用所有bean的构建方法getBean(),所以当项目启动好后,容器内所有对象都已被构建好了。
“我个人习惯”将spring容器启动分为三个阶段:(1)容器初始化阶段、(2)Bean实例化(instantiation)阶段、(3)Bean初始化(initialization)阶段。
- 容器初始化阶段:这个阶段主要是通过某些工具类加载Configuration MetaData,并将相关信息解析成BeanDefinition注册到BeanDefinitionRegistry中,即对象管理信息的收集。同时也会进行各种处理器的注册、上下文的初始化、事件广播的初始化等等准备工作。
- Bean实例化(instantiation)阶段:这个阶段主要是Bean的实例化,其实就是Bean对象的构造。不过此时构造出的对象,他们内部的依赖对象仍然没有注入,只是通过反射(或Cglib)生成了具体对象的实例(执行构造函数),其实有点类似于我们手动new对象,new出的对象已经执行过了构造函数,并且内部的基本数据也已经准备好了,但如果内部还有其他对象的依赖,就需要后续的流程去主动注入。
- Bean初始化(initialization)阶段:这个阶段主要是对实例化好后的Bean进行依赖注入的过程。同时还会执行用户自定义的一些初始化方法,注册Bean的销毁方法、缓存初始化好的Bean等。
接下来我们主要围绕这三个阶段通过Spring源码来看容器初始化的具体过程(SpringBoot项目)。
一.容器初始化阶段
ApplicationContext在容器启动时会自动帮我们构建好所有的Bean,所以当我们项目启动好后,容器内所有对象都已经构建好了。其主要的逻辑都在refresh()函数中,所以我们也从这个函数开始看,下面是refresh()的主体逻辑。
我们简单介绍下几个重要函数:
- prepareRefresh():
初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验,如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,重写initPropertySources方法就好了。 - postProcessBeanFactory()、invokeBeanFactoryPostProcessors():
spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制,这种机制可以让我们在Bean实例化前,对注册到容器的BeanDefinition所保存的信息进行修改。上面两个函数就主要是BeanFactoryPostProcessor的相关操作。值得一提的是我们经常会在properties文件中配置一些属性值,然后通过${}的占位符去替换xml中的一些变量,这个过程就是BeanFactoryPostProcessor帮我实现的。 - registerBeanPostProcessors:
注册BeanPostProcessor。BeanPostProcessor应该是大名鼎鼎的了,它和BeanFactoryPostProcessor相对应,BeanFactoryPostProcessor主要是对BeanDefinition进行相关操作,而BeanPostProcessor主要是插手Bean的构建过程。BeanPostProcessor是个接口,它有很多实现类,接下来我们会逐渐接触到BeanPostProcessor的不同实现类。 - finishBeanFactoryInitialization:
初始化剩下的单例Bean(非延迟加载的)。上面的函数中构建了各种系统使用的Bean,而剩下的Bean都会在finishBeanFactoryInitialization中构建,这其中就包括我们在应用中声明的各种业务Bean,Bean的实例化和初始化过程逻辑都在这个函数中。
我想特别介绍下invokeBeanFactoryPostProcessors(beanFactory),我们都知道Spring会帮我们自动扫描出所有我们需要放入容器的Bean,其具体的实现就在这里。里面会执行所有的BeanFactoryPostProcessors,Spring帮我们定义了一个ConfigurationClassPostProcessor,它的函数postProcessBeanDefinitionRegistry会在这个阶段被执行,里面会根据我们配置的scanning-path把我们所有使用注解(@Component、@Service等…)注释的Bean解析成BeanDefinition,并记录Bean的信息到容器中。
Spring容器的启动阶段就介绍到这里,因为具体的逻辑实在实在太繁杂,而我也就知道个梗概,有想要想要详细了解的同学,可以一个一个方法的去学习了解下。接下来是我们本文的重点内容:bean的实例化和初始化过程。
二.Bean的实例化和初始化
大家总是会错误的理解Bean的“实例化”和“初始化”过程,总会以为初始化就是对象执行构造函数生成对象实例的过程,其实不然,在初始化阶段实际对象已经实例化出来了,初始化阶段进行的是依赖的注入和执行一些用户自定义的初始化逻辑。对于Bean的构建过程,网上有个非常经典的流程图如下:
上面的流程图并不是十分准确,但却把整个bean的构建、使用、销毁流程整体勾勒出来了,大家可以有个初步的整体的印象。实际上Bean的构建流程非常复杂,下面会从源码角度讲解,内容会很多也很庞杂,我把重要的方法和逻辑做了一个简单的总结,让大家可以从方法角度有个整体了解,接下来我们就开始从bean的实例化流程开始讲起。
(1)Bean的实例化
我们接着上面从finishBeanFactoryInitialization函数看起,因为我们定义的业务Bean实例化和初始化都是在这个函数中实现的。我们进入finishBeanFactoryInitialization函数内部。其中大部分的函数是Bean构建的准备逻辑,实际的构建逻辑在preInstantiateSingletons中,我们继续往下,看preInstantiateSingletons函数。
函数会先获取所有需要构建的Bean名称,之前提过在Spring容器启动阶段Bean的相关信息就会被封装成BeanDefinition并注册到容器中。通过bean的RootBeanDefinition判断该bean是否为可构建的类型,很明显可构建的Bean不能是抽象类,不能是接口,也不能是懒加载的bean。之后会判断对象是不是FactoryBean,FactoryBean是一种特殊的Bean,需要特殊处理。
FactoryBean简介
在我们平时的开发中经常会使用第三方库相关类,为了避免接口和实现类的耦合,我们通常会使用工厂模式。提供一个工厂类来实例化具体接口实现类。主体对象只需要注入工厂类,具体接口实现类有变更时,我们只需变更工厂类,而主体类无需做任何改动。针对上面的场景,spring为我们提供了更方便的工具FactoryBean,当某些第三方库不能直接注册到spring容器时,就可以实现org.springframework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑。
针对FactoryBean而言,其主语是Bean,定语为Factory,它本身也只是一个bean而已,只不过这种类型的bean本身就是用来生产对象的工厂。下面是FactoryBean接口代码,FactoryBean只定义了三个方法,getObject()返回我们需要生产的对象, getObjectType()返回生产对象的类型,isSingleton()用于标识该对象是否以单例形式存在于容器中。当我们从容器中获取FactoryBean类型的Bean时,容器返回的是FactoryBean所“生产”的对象类型,而非FactoryBean实现本身。
public interface FactoryBean {
T getObject();
Class<?> getObjectType();
default boolean isSingleton();
}
然后我们进入getBean(beanName),我们的业务Bean就是通过这个函数构建的。
经过一次跳转后我们来到了org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean函数,这个函数还是蛮重要的,接下来我们详细介绍下:
在函数内首先会将传递进来的name通过transformedBeanName()函数转换成了beanName,这个函数主要有两个功能:1: 特殊处理FactoryBean的名称、2: 解决别名的问题。
之后我们会看见getSingleton(beanName)函数,这个函数灰常重要,它是用来解决循环依赖的。举个例子:有两个Bean A和B,这两个Bean间存在相互依赖。那么当容器构建A时,实例化好A后,容器会把尚未注入依赖的A暂时放入缓存中,然后去帮A注入依赖。这时容器发现A依赖于B,但是B还没有构建好,那么就会先去构建B。容器在实例化好B时,同样需要帮B注入依赖,此时B发现自己依赖A,在获取A时就可以通过getSingleton(beanName)获取尚未注入依赖的A引用,这样就不会阻塞B的构建,B完成构建后就又可以注入到A中,这样就解决了简单的循环依赖问题。当然循环依赖的问题场景很多,出现的情景也不同,上面举的例子只是最简单的一种,后面会对常见的循环依赖问题进行总结。
在之后会通过isPrototypeCurrentlyInCreation(beanName)检测Prototype类型的Bean是否已经正在构建。说到这里,我们首先需要明确的一点是:Prototype类型Bean不支持循环引用 (包括自用引用),而Singleton类型的bean是可以循环引用的 (包括自用引用),这里说的自我引用指的是自己注入自己。其实原因也不难理解,因为Prototype类型的bean是即取即用的,容器不会维护它的生命周期,更不会保存它的引用,那么就不会像Singleton类型的bean一样,在容器中有一份缓存,其实Prototype类型bean也完全没必要有缓存,毕竟容器无需维护它的生命周期。
那么代码执行到这里一但发现Prototype类型的bean已经在构建了,便说明出现了循环引用,便直接抛出异常。举个小荔枝如下:
在spring容器中会根据bean定义的scope来创建实例对象,其中我们比较常用的scope定义有两种即:singleton和prototype。
- singleton:
标记为singleton的bean在容器中只存在一个实例,所有对该对象的引用将共享这个实例。该实例从容器启动构建好后,就一直存活到容器退出,与容器“几乎”拥有相同的“寿命”。- prototype:
容器在接收到该类型对象的请求时,会每次都重新生成一个新的对象实例给请求方。当容器将对象实例返回给请求方后,容器就不再拥有当前返回对象的引用,请求方需要自己负责该对象后续生命周期的管理。有点类似于我们使用new创建对象,但区别在于我们用new创建的对像无法进行依赖注入,需要我们手动注入依赖,而prototype类型bean的实例化和依赖注入容器都会帮我们处理。有一点值得注意的是:当一个单例类型的bean引用一个prototype类型的bean时,prototype类型的bean也会变成单例。这主要是因为单例bean的依赖注入只会在容器启动时执行一次。如果要解决这个问题,可以改为手动调用getBean方法,也可以使用@lookup注解。
再接下来的逻辑其实就比较清晰了,首先获取Bean的定义信息,然后判断下是否有DependsOn依赖的Bean,没错这里就是@DependsOn注解发挥作用的地方,如果我们的Bean在构建前必须要保证某个Bean已经构建好,那么我们就可以使用这个@DependsOn这个注解。我们可以看到实现逻辑其实很简单,就是取出所有依赖的Bean,然后逐个调用getBean接口依次构建。在处理好依赖的Bean后,容器会根据Bean的类型进行构建,Bean主要有Singleton和Prototype两种类型,当然还有很多我们不常用的类型,这里我们就不管了。因为一般我们使用的Bean都是Singleton类型的,所以我们就直接看Singleton类型bean的构建方法。
我们可以看到singleton类型bean的构建调用了DefaultSingletonBeanRegistry实现的getSingleton方法。该方法有两个参数,第一个为bean的名字beanName,第二个为ObjectFactory<?> singletonFactory,是一个函数式接口,该函数式接口之后会在getSingleton方法中使用,该函数式接口的主要实现是createBean方法。
下面是DefaultSingletonBeanRegistry#getSingleton函数的实现代码,其核心的四个函数为:
// 前置检查,通过构建中bean集合(singletonsCurrentlyInCreation)来检测该bean是否进行了重复构建,
// 可以防止构造器注入可能导致的循环引用问题
beforeSingletonCreation(beanName);
// 调用函数式接口ObjectFactory的具体实现逻辑,即上文中getSingleton方法的第二个参数,主要实现就是createBean方法
singletonFactory.getObject();
// 后置处理,将当前bean从构建中bean集合(singletonsCurrentlyInCreation)中移除。
afterSingletonCreation(beanName);
// 把结果存在singletonObjects中,并删除一些用于处理循环引用的中间状态
addSingleton(beanName, singletonObject);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
// 查看单例bean是否已经构建好,凡是构建好的单例bean,都会存在Map<String, Object> singletonObjects里面
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// this.singletonsCurrentlyInDestruction是个boolean类型,记录的是当前这个单例bean是否正在被销毁,
// 如果是true,代表单例bean已经执行了自身的destroy销毁方法,或者有异常的时候执行了destroySingleton方法等情况
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 前置检查,通过构建中bean集合(singletonsCurrentlyInCreation)来检测该bean是否进行了重复构建,
// 可以防止构造器注入可能导致的循环引用问题
beforeSingletonCreation(beanName);
boolean newSingleton = false;
// this.suppressedExceptions是个Set,这个Set里面存放着各种异常信息
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
// 如果此时suppressedExceptions为空,就new LinkedHashSet<>()来保存接下来可能发生的异常
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 调用ObjectFactory函数式方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// 看这个时候单例是否已经被创建了,如果是的话,不报错,继续执行
singletonObject = this.singletonObjects.get(beanName);
// 如果没有被创建,报错
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
// 把出现过的异常加进去
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 后置处理,将当前bean从构建中bean集合(singletonsCurrentlyInCreation)中移除。
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 把结果存在singletonObjects中,并删除一些中间状态
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
接下来我们进入createBean(beanName, mbd, args)函数中,具体代码如下所示:
函数内首先做了些准备工作,包括:根据设置的class属性和className解析class、对override属性进行标记和验证(这里需要注意的是Spring的配置里面根本没有override-method之类的配置,但是在spring配置中存在lookup-method和replace-method,这两个配置会被统一存放在beanDefinition中的methodOverrides属性里,这个方法就是对这两个配置做操作)。在这之后会执行resolveBeforeInstantiation函数,千万不要被它上面的注释误导了,这个函数并不是用来生成我们 “通常意义” 上的动态代理的,我们 “通常意义” 上的动态代理实现在后面会有介绍。
我们可以看到resolveBeforeInstantiation里面实际上调用了applyBeanPostProcessorsBeforeInstantiation接口,在该接口里面会执行所有InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法。
在这我们先介绍下InstantiationAwareBeanPostProcessor,大家应该都知道BeanPostProcessor接口,这个接口作用于Bean的构建过程,可以对Bean进行一些自定义的修改,比如属性值的修改、构造器的选择等等。而InstantiationAwareBeanPostProcessor接口继承自BeanPostProcessor接口。新增了3个方法,其类图如下:
通过名字我们就可以知道,新增的3个接口中前两个分别作用于Bean实例化前和实例化后,最后一个接口postProcessPropertyValues作用于Bean的初始化阶段,主要用来设置对象属性(依赖注入),其中我们最常用的注入依赖对象的@Autowired注解,就是通过实现这个接口来进行依赖注入的。
我们接着介绍applyBeanPostProcessorsBeforeInstantiation方法,我们可以看到该方法会找到所有InstantiationAwareBeanPostProcessor类型的BeanPostProcessor,然后调用其实现的postProcessBeforeInstantiation的方法。我们最常见的该方法的一个实现类是AbstractAutoProxyCreator,我们看下其具体实现:
这里shouldSkip(beanClass, beanName)方法是一个很容易忽略的方法,但这个方法第一次被调用时把所有的切面(Advisor)加载完成。另外我们可以看到,如果我们有自定义的TargetSource,那么在这里就会直接创建代理对象。postProcessBeforeInstantiation()返回对象后,会对该对象执行所有BeanPostProcessor的postProcessorAfterInitialization()方法,最后直接返回对象给用户。
TargetSource相当于代理中目标对象的容器。当方法调用穿过所有切面逻辑,到达调用链的终点时,本该调用目标对象的方法了。但此时Spring AOP做了点手脚,他并不是直接调用这个目标对象的方法,而是需要通过TargetSource的getTarget()方法获取目标对象,然后再调用目标对象的相应方法。这种方式使得TargetSource拥有了目标对象的控制权,可以控制每次方法调用实际作用到的具体对象实例。
我们继续往下走进入doCreateBean(beanName, mbdToUse, args)方法。在spring里有个有意思的现象:大部分核心功能的实际实现函数都是以do开头的。现在我们走到了doCreateBean函数,通过名字我们就可以知道该函数用来构建Bean,而实际上Bean实例化和初始化的核心逻辑都在doCreateBean方法中,所以接下来的所有讲解都是围绕doCreateBean方法进行的。
/** * Actually create the specified bean. Pre-creation processing has already happened * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a new instance of the bean * @throws BeanCreationException if the bean could not be created * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
<span class="token comment">// Instantiate the bean.</span> <span class="token class-name">BeanWrapper</span> instanceWrapper <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 用于处理factoryBean</span> instanceWrapper <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>factoryBeanInstanceCache<span class="token punctuation">.</span><span class="token keyword">remove</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instanceWrapper <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> instanceWrapper <span class="token operator">=</span> <span class="token function">createBeanInstance</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> final <span class="token class-name">Object</span> bean <span class="token operator">=</span> instanceWrapper<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> beanType <span class="token operator">=</span> instanceWrapper<span class="token punctuation">.</span><span class="token function">getWrappedClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanType <span class="token operator">!=</span> NullBean<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> mbd<span class="token punctuation">.</span>resolvedTargetType <span class="token operator">=</span> beanType<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Allow post-processors to modify the merged bean definition.</span> synchronized <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>postProcessingLock<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mbd<span class="token punctuation">.</span>postProcessed<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token function">applyMergedBeanDefinitionPostProcessors</span><span class="token punctuation">(</span>mbd<span class="token punctuation">,</span> beanType<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Post-processing of merged bean definition failed"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> mbd<span class="token punctuation">.</span>postProcessed <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Eagerly cache singletons to be able to resolve circular references</span> <span class="token comment">// even when triggered by lifecycle interfaces like BeanFactoryAware.</span> boolean earlySingletonExposure <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token keyword">this</span><span class="token punctuation">.</span>allowCircularReferences <span class="token operator">&&</span> <span class="token function">isSingletonCurrentlyInCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonExposure<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Eagerly caching bean '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span> <span class="token string">"' to allow for resolving potential circular references"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">addSingletonFactory</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Initialize the bean instance.</span> <span class="token class-name">Object</span> exposedObject <span class="token operator">=</span> bean<span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token function">populateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> instanceWrapper<span class="token punctuation">)</span><span class="token punctuation">;</span> exposedObject <span class="token operator">=</span> <span class="token function">initializeBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> exposedObject<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ex instanceof BeanCreationException <span class="token operator">&&</span> beanName<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>BeanCreationException<span class="token punctuation">)</span> ex<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getBeanName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token punctuation">(</span>BeanCreationException<span class="token punctuation">)</span> ex<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span> mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Initialization of bean failed"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonExposure<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token class-name">Object</span> earlySingletonReference <span class="token operator">=</span> <span class="token function">getSingleton</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token keyword">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonReference <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exposedObject <span class="token operator">==</span> bean<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> exposedObject <span class="token operator">=</span> earlySingletonReference<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>allowRawInjectionDespiteWrapping <span class="token operator">&&</span> <span class="token function">hasDependentBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> String<span class="token punctuation">[</span><span class="token punctuation">]</span> dependentBeans <span class="token operator">=</span> <span class="token function">getDependentBeans</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> Set<span class="token operator"><</span>String<span class="token operator">></span> actualDependentBeans <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span>dependentBeans<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> dependentBean <span class="token punctuation">:</span> dependentBeans<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">removeSingletonIfCreatedForTypeCheckOnly</span><span class="token punctuation">(</span>dependentBean<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> actualDependentBeans<span class="token punctuation">.</span><span class="token keyword">add</span><span class="token punctuation">(</span>dependentBean<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>actualDependentBeans<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCurrentlyInCreationException</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token string">"Bean with name '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span> <span class="token string">"' has been injected into other beans ["</span> <span class="token operator">+</span> StringUtils<span class="token punctuation">.</span><span class="token function">collectionToCommaDelimitedString</span><span class="token punctuation">(</span>actualDependentBeans<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"] in its raw version as part of a circular reference, but has eventually been "</span> <span class="token operator">+</span> <span class="token string">"wrapped. This means that said other beans do not use the final version of the "</span> <span class="token operator">+</span> <span class="token string">"bean. This is often the result of over-eager type matching - consider using "</span> <span class="token operator">+</span> <span class="token string">"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Register bean as disposable.</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token function">registerDisposableBeanIfNecessary</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> bean<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">BeanDefinitionValidationException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span> mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Invalid destruction signature"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> exposedObject<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
这个函数的主体逻辑如下:
- 实例化bean,并封装进BeanWrapper中
- 执行所有MergedBeanDefinitionPostProcessor处理器
- 进行依赖注入
- 执行初始化逻辑,及对bean进行属性修改及切面增强
- 注册Bean的销毁逻辑
- 返回构建好的bean
BeanWrapper相当于一个代理器,Spring委托BeanWrapper完成Bean属性的填充工作。BeanWrapper继承了PropertyAccessor和PropertyEditorRegistry。BeanWrapperImpl是BeanWrapper接口的默认实现,它会缓存Bean的内省结果而提高效率。BeanWrapper对bean实例的操作很方便,可以免去直接使用java反射API带来的繁琐。
factoryBean的处理逻辑忽略,接下来我们看createBeanInstance方法,该方法主要用来实例化bean,bean实例化的核心逻辑就在该方法中。
/** * Create a new instance for the specified bean, using an appropriate instantiation strategy: * factory method, constructor autowiring, or simple instantiation. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a BeanWrapper for the new instance * @see #obtainFromSupplier * @see #instantiateUsingFactoryMethod * @see #autowireConstructor * @see #instantiateBean */ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. Class<?> beanClass = resolveBeanClass(mbd, beanName); // 确保class不为空,并且访问权限为public if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); }
Supplier<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> instanceSupplier <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getInstanceSupplier</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instanceSupplier <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token function">obtainFromSupplier</span><span class="token punctuation">(</span>instanceSupplier<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getFactoryMethodName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token function">instantiateUsingFactoryMethod</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Shortcut when re-creating the same bean...</span> <span class="token comment">// 一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器</span> <span class="token comment">// 在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中,避免再次创建相同bean时再次解析</span> boolean resolved <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span> boolean autowireNecessary <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>args <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> synchronized <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>constructorArgumentLock<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>resolvedConstructorOrFactoryMethod <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> resolved <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span> autowireNecessary <span class="token operator">=</span> mbd<span class="token punctuation">.</span>constructorArgumentsResolved<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolved<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>autowireNecessary<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token function">autowireConstructor</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token function">instantiateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Candidate constructors for autowiring?</span> <span class="token comment">// 需要根据参数解析、确定构造函数</span> Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">[</span><span class="token punctuation">]</span> ctors <span class="token operator">=</span> <span class="token function">determineConstructorsFromBeanPostProcessors</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 解析的构造器不为空 || 注入类型为构造函数自动注入 || bean定义中有构造器参数 || 传入参数不为空</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ctors <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">||</span> mbd<span class="token punctuation">.</span><span class="token function">getResolvedAutowireMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> AUTOWIRE_CONSTRUCTOR <span class="token operator">||</span> mbd<span class="token punctuation">.</span><span class="token function">hasConstructorArgumentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token operator">!</span>ObjectUtils<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token function">autowireConstructor</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> ctors<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Preferred constructors for default construction?</span> ctors <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getPreferredConstructors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ctors <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token function">autowireConstructor</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> ctors<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// No special handling: simply use no-arg constructor.</span> <span class="token keyword">return</span> <span class="token function">instantiateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
上面方法的目的就是选出一个策略来实例化一个对象,那有什么策略呢? 这就看程序员是怎么配置的了,程序员可以配置工厂方法,指定构造方法,或者是程序员没有做出任何干涉,让Spring按自己的方式去实例化。
上面代码的主要逻辑如下:
- 如果bean定义中存在 InstanceSupplier ,会使用这个回调接口创建对象(应该是3.X以后新加的,3.X的源码中没有)
- 根据配置的factoryMethodName或factory-method创建bean(通过工厂方法创建对象)
- 解析构造函数并进行实例化
通过回调接口或工厂方法创建对象的情况,我们接触的比较少,我们这里主要介绍使用构造器创建bean实例。
因为一个类可能有多个构造函数,所以需要根据配置文件中配置的参数或传入的参数确定最终调用的构造函数,因为判断过程会比较消耗性能,所以Spring会将解析、确定好的构造函数缓存到BeanDefinition中的resolvedConstructorOrFactoryMethod字段中。在下次创建相同bean的时候,会直接从RootBeanDefinition中的属性resolvedConstructorOrFactoryMethod缓存的值获取,避免再次解析。
determineConstructorsFromBeanPostProcessors方法会帮我们选择合适的构造器,我们进该方法中:
在这段代码中会使用SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor选择合适的构造器,目前该功能主要由SmartInstantiationAwareBeanPostProcessor的子类AutowiredAnnotationBeanPostProcessor实现,下面是相关代码。
@Override @Nullable public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException {
<span class="token comment">// Let's check for lookup methods here...</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>lookupMethodsChecked<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>AnnotationUtils<span class="token punctuation">.</span><span class="token function">isCandidateClass</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">,</span> Lookup<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> targetClass <span class="token operator">=</span> beanClass<span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token punctuation">{<!-- --></span> ReflectionUtils<span class="token punctuation">.</span><span class="token function">doWithLocalMethods</span><span class="token punctuation">(</span>targetClass<span class="token punctuation">,</span> method <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{<!-- --></span> Lookup lookup <span class="token operator">=</span> method<span class="token punctuation">.</span><span class="token function">getAnnotation</span><span class="token punctuation">(</span>Lookup<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>lookup <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Assert<span class="token punctuation">.</span><span class="token function">state</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory <span class="token operator">!=</span> null<span class="token punctuation">,</span> <span class="token string">"No BeanFactory available"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> LookupOverride override <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LookupOverride</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> lookup<span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> RootBeanDefinition mbd <span class="token operator">=</span> <span class="token punctuation">(</span>RootBeanDefinition<span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">.</span><span class="token function">getMergedBeanDefinition</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> mbd<span class="token punctuation">.</span><span class="token function">getMethodOverrides</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addOverride</span><span class="token punctuation">(</span>override<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchBeanDefinitionException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token string">"Cannot apply @Lookup to beans without corresponding bean definition"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> targetClass <span class="token operator">=</span> targetClass<span class="token punctuation">.</span><span class="token function">getSuperclass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>targetClass <span class="token operator">!=</span> null <span class="token operator">&&</span> targetClass <span class="token operator">!=</span> Object<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IllegalStateException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token string">"Lookup method resolution failed"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>lookupMethodsChecked<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
//先查找缓存
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
//若是缓存中没有
if (candidateConstructors null) {
// Fully synchronized resolution now…
//同步此方法
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
//双重判断,避免多线程并发问题
if (candidateConstructors null) {
Constructor<?>[] rawCandidates;
try {
//获取此Bean的所有构造器
rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, “Resolution of declared constructors on bean Class [” + beanClass.getName() + “] from ClassLoader [” + beanClass.getClassLoader() + “] failed”, ex);
}
//最终适用的构造器集合
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
//存放依赖注入的required=true的构造器
Constructor<?> requiredConstructor = null;
//存放默认构造器
Constructor<?> defaultConstructor = null;
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
int nonSyntheticConstructors = 0;
for (Constructor<?> candidate : rawCandidates) {
if (!candidate.isSynthetic()) {
nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
continue;
}
//查找当前构造器上的注解
AnnotationAttributes ann = findAutowiredAnnotation(candidate);
//若没有注解
if (ann null) {
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found…
}
}
}
//若有注解
if (ann != null) {
//已经存在一个required=true的构造器了,抛出异常
if (requiredConstructor != null) {
throw new BeanCreationException(beanName, "Invalid autowire-marked constructor: “ + candidate + ”. Found constructor with ‘required’ Autowired annotation already: " + requiredConstructor);
}
//判断此注解上的required属性
boolean required = determineRequiredStatus(ann);
//若为true
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName, "Invalid autowire-marked constructors: “ + candidates + ”. Found constructor with ‘required’ Autowired annotation: " + candidate);
}
//将当前构造器加入requiredConstructor集合
requiredConstructor = candidate;
}
//加入适用的构造器集合中
candidates.add(candidate);
}
//如果该构造函数上没有注解,再判断构造函数上的参数个数是否为0
else if (candidate.getParameterCount() 0) {
//如果没有参数,加入defaultConstructor集合
defaultConstructor = candidate;
}
}
//适用的构造器集合若不为空
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
//若没有required=true的构造器
if (requiredConstructor null) {
if (defaultConstructor != null) {
//将defaultConstructor集合的构造器加入适用构造器集合
candidates.add(defaultConstructor);
}
else if (candidates.size() 1 && logger.isInfoEnabled()) {
logger.info(“Inconsistent constructor declaration on bean with name '” + beanName +
"': single autowire-marked constructor flagged as optional - " +
"this constructor is effectively required since there is no " +
"default constructor to fall back to: " + candidates.get(0));
}
}
//将适用构造器集合赋值给将要返回的构造器集合
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
//如果适用的构造器集合为空,且Bean只有一个构造器并且此构造器参数数量大于0
else if (rawCandidates.length 1 && rawCandidates[0].getParameterCount() > 0) {
//就使用此构造器来初始化
candidateConstructors = new Constructor<?>[] {
rawCandidates[0]};
}
//如果构造器有两个,且默认构造器不为空
else if (nonSyntheticConstructors 2 && primaryConstructor != null &&
defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
//使用默认构造器返回
candidateConstructors = new Constructor<?>[] {
primaryConstructor, defaultConstructor};
}
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
candidateConstructors = new Constructor<?>[] {
primaryConstructor};
}
else {
//上述都不符合的话,只能返回一个空集合了
candidateConstructors = new Constructor<?>[0];
}
//放入缓存,方便下一次调用
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
//返回candidateConstructors集合,若为空集合返回null
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
上面的代码首先会查看这个将被实例化的对象中有没有添加了@Lookup注解的方法,有的话为这种方法生成代理。之后会循环遍历所有的构造方法,以选取合适的构造方法。这里面的逻辑非常复杂,这里就不做介绍了。
这里顺便提下,在Spring中当我们使用lombok的@Builder注解时,该类默认不带无参构造函数。 若此时使用fastJson将json字符串转换为实体类,将抛出异常,需要增加@NoArgsConstructor注解防止出现异常。
在选定好了构造参数后,我们接下来看对象的实例化过程,Spring的实例化可以分为 【带参实例化】 及 【无参实例化】 。我们先来看下带参实例化方法autowireConstructor:
/** * "autowire constructor" (with constructor arguments by type) behavior. * Also applied if explicit constructor argument values are specified, * matching all remaining arguments with beans from the bean factory. * <p>This corresponds to constructor injection: In this mode, a Spring * bean factory is able to host components that expect constructor-based * dependency resolution. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param chosenCtors chosen candidate constructors (or {@code null} if none) * @param explicitArgs argument values passed in programmatically via the getBean method, * or {@code null} if none (-> use constructor argument values from bean definition) * @return a BeanWrapper for the new instance */ public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
<span class="token class-name">BeanWrapperImpl</span> bw <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BeanWrapperImpl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">.</span><span class="token function">initBeanWrapper</span><span class="token punctuation">(</span>bw<span class="token punctuation">)</span><span class="token punctuation">;</span> Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> constructorToUse <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token class-name">ArgumentsHolder</span> argsHolderToUse <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> argsToUse <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>explicitArgs <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> argsToUse <span class="token operator">=</span> explicitArgs<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> argsToResolve <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> synchronized <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>constructorArgumentLock<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> constructorToUse <span class="token operator">=</span> <span class="token punctuation">(</span>Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">)</span> mbd<span class="token punctuation">.</span>resolvedConstructorOrFactoryMethod<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>constructorToUse <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> mbd<span class="token punctuation">.</span>constructorArgumentsResolved<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Found a cached constructor...</span> argsToUse <span class="token operator">=</span> mbd<span class="token punctuation">.</span>resolvedConstructorArguments<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>argsToUse <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> argsToResolve <span class="token operator">=</span> mbd<span class="token punctuation">.</span>preparedConstructorArguments<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>argsToResolve <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> argsToUse <span class="token operator">=</span> <span class="token function">resolvePreparedArguments</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> constructorToUse<span class="token punctuation">,</span> argsToResolve<span class="token punctuation">,</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>constructorToUse <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> argsToUse <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Take specified constructors, if any.</span> Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">[</span><span class="token punctuation">]</span> candidates <span class="token operator">=</span> chosenCtors<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>candidates <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> beanClass <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getBeanClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> candidates <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isNonPublicAccessAllowed</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span> beanClass<span class="token punctuation">.</span><span class="token function">getDeclaredConstructors</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> beanClass<span class="token punctuation">.</span><span class="token function">getConstructors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Resolution of declared constructors on bean Class ["</span> <span class="token operator">+</span> beanClass<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"] from ClassLoader ["</span> <span class="token operator">+</span> beanClass<span class="token punctuation">.</span><span class="token function">getClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"] failed"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>candidates<span class="token punctuation">.</span>length <span class="token operator">==</span> <span class="token number">1</span> <span class="token operator">&&</span> explicitArgs <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">hasConstructorArgumentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> uniqueCandidate <span class="token operator">=</span> candidates<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>uniqueCandidate<span class="token punctuation">.</span><span class="token function">getParameterCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> synchronized <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>constructorArgumentLock<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> mbd<span class="token punctuation">.</span>resolvedConstructorOrFactoryMethod <span class="token operator">=</span> uniqueCandidate<span class="token punctuation">;</span> mbd<span class="token punctuation">.</span>constructorArgumentsResolved <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span> mbd<span class="token punctuation">.</span>resolvedConstructorArguments <span class="token operator">=</span> EMPTY_ARGS<span class="token punctuation">;</span> <span class="token punctuation">}</span> bw<span class="token punctuation">.</span><span class="token function">setBeanInstance</span><span class="token punctuation">(</span><span class="token function">instantiate</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> uniqueCandidate<span class="token punctuation">,</span> EMPTY_ARGS<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> bw<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Need to resolve the constructor.</span> boolean autowiring <span class="token operator">=</span> <span class="token punctuation">(</span>chosenCtors <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">||</span> mbd<span class="token punctuation">.</span><span class="token function">getResolvedAutowireMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> AutowireCapableBeanFactory<span class="token punctuation">.</span>AUTOWIRE_CONSTRUCTOR<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ConstructorArgumentValues</span> resolvedValues <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">int</span> minNrOfArgs<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>explicitArgs <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> minNrOfArgs <span class="token operator">=</span> explicitArgs<span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token class-name">ConstructorArgumentValues</span> cargs <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getConstructorArgumentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> resolvedValues <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConstructorArgumentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> minNrOfArgs <span class="token operator">=</span> <span class="token function">resolveConstructorArguments</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> cargs<span class="token punctuation">,</span> resolvedValues<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> AutowireUtils<span class="token punctuation">.</span><span class="token function">sortConstructors</span><span class="token punctuation">(</span>candidates<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> minTypeDiffWeight <span class="token operator">=</span> Integer<span class="token punctuation">.</span>MAX_VALUE<span class="token punctuation">;</span> Set<span class="token operator"><</span>Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token operator">></span> ambiguousConstructors <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> LinkedList<span class="token operator"><</span>UnsatisfiedDependencyException<span class="token operator">></span> causes <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>Constructor<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> candidate <span class="token punctuation">:</span> candidates<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">[</span><span class="token punctuation">]</span> paramTypes <span class="token operator">=</span> candidate<span class="token punctuation">.</span><span class="token function">getParameterTypes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>constructorToUse <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> argsToUse <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> argsToUse<span class="token punctuation">.</span>length <span class="token operator">></span> paramTypes<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Already found greedy constructor that can be satisfied -></span> <span class="token comment">// do not look any further, there are only less greedy constructors left.</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>paramTypes<span class="token punctuation">.</span>length <span class="token operator"><</span> minNrOfArgs<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token class-name">ArgumentsHolder</span> argsHolder<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedValues <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> String<span class="token punctuation">[</span><span class="token punctuation">]</span> paramNames <span class="token operator">=</span> ConstructorPropertiesChecker<span class="token punctuation">.</span><span class="token function">evaluate</span><span class="token punctuation">(</span>candidate<span class="token punctuation">,</span> paramTypes<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>paramNames <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token class-name">ParameterNameDiscoverer</span> pnd <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">.</span><span class="token function">getParameterNameDiscoverer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pnd <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> paramNames <span class="token operator">=</span> pnd<span class="token punctuation">.</span><span class="token function">getParameterNames</span><span class="token punctuation">(</span>candidate<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> argsHolder <span class="token operator">=</span> <span class="token function">createArgumentArray</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> resolvedValues<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> paramTypes<span class="token punctuation">,</span> paramNames<span class="token punctuation">,</span> <span class="token function">getUserDeclaredConstructor</span><span class="token punctuation">(</span>candidate<span class="token punctuation">)</span><span class="token punctuation">,</span> autowiring<span class="token punctuation">,</span> candidates<span class="token punctuation">.</span>length <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">UnsatisfiedDependencyException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Ignoring constructor ["</span> <span class="token operator">+</span> candidate <span class="token operator">+</span> <span class="token string">"] of bean '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span> <span class="token string">"': "</span> <span class="token operator">+</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Swallow and try next constructor.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>causes <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> causes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedList</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> causes<span class="token punctuation">.</span><span class="token keyword">add</span><span class="token punctuation">(</span>ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Explicit arguments given -> arguments length must match exactly.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>paramTypes<span class="token punctuation">.</span>length <span class="token operator">!=</span> explicitArgs<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> argsHolder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentsHolder</span><span class="token punctuation">(</span>explicitArgs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">int</span> typeDiffWeight <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isLenientConstructorResolution</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span> argsHolder<span class="token punctuation">.</span><span class="token function">getTypeDifferenceWeight</span><span class="token punctuation">(</span>paramTypes<span class="token punctuation">)</span> <span class="token punctuation">:</span> argsHolder<span class="token punctuation">.</span><span class="token function">getAssignabilityWeight</span><span class="token punctuation">(</span>paramTypes<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Choose this constructor if it represents the closest match.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>typeDiffWeight <span class="token operator"><</span> minTypeDiffWeight<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> constructorToUse <span class="token operator">=</span> candidate<span class="token punctuation">;</span> argsHolderToUse <span class="token operator">=</span> argsHolder<span class="token punctuation">;</span> argsToUse <span class="token operator">=</span> argsHolder<span class="token punctuation">.</span>arguments<span class="token punctuation">;</span> minTypeDiffWeight <span class="token operator">=</span> typeDiffWeight<span class="token punctuation">;</span> ambiguousConstructors <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>constructorToUse <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> typeDiffWeight <span class="token operator">==</span> minTypeDiffWeight<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ambiguousConstructors <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> ambiguousConstructors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ambiguousConstructors<span class="token punctuation">.</span><span class="token keyword">add</span><span class="token punctuation">(</span>constructorToUse<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> ambiguousConstructors<span class="token punctuation">.</span><span class="token keyword">add</span><span class="token punctuation">(</span>candidate<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>constructorToUse <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>causes <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token class-name">UnsatisfiedDependencyException</span> ex <span class="token operator">=</span> causes<span class="token punctuation">.</span><span class="token function">removeLast</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> cause <span class="token punctuation">:</span> causes<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">.</span><span class="token function">onSuppressedException</span><span class="token punctuation">(</span>cause<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">throw</span> ex<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Could not resolve matching constructor "</span> <span class="token operator">+</span> <span class="token string">"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ambiguousConstructors <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isLenientConstructorResolution</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Ambiguous constructor matches found in bean '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span> <span class="token string">"' "</span> <span class="token operator">+</span> <span class="token string">"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): "</span> <span class="token operator">+</span> ambiguousConstructors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>explicitArgs <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">&&</span> argsHolderToUse <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> argsHolderToUse<span class="token punctuation">.</span><span class="token function">storeCache</span><span class="token punctuation">(</span>mbd<span class="token punctuation">,</span> constructorToUse<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> Assert<span class="token punctuation">.</span><span class="token function">state</span><span class="token punctuation">(</span>argsToUse <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">"Unresolved constructor arguments"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> bw<span class="token punctuation">.</span><span class="token function">setBeanInstance</span><span class="token punctuation">(</span><span class="token function">instantiate</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> constructorToUse<span class="token punctuation">,</span> argsToUse<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> bw<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
上面代码非常长,总体的功能逻辑如下:
- 确定实例化时构造器传入的参数:
(1)如果调用getBean方式时传入的参数不为空,则可以直接使用传入的参数。
(2)否则尝试从缓存中获取参数。
(3)否则解析xml中配置的构造器参数。 - 确定使用的构造函数:
根据第一步中确定下来的参数,接下来的任务就是根据参数的个数、类型来确定最终调用的构造函数。首先是根据参数个数匹配,把所有构造函数根据参数个数升序排序,筛选出参数个数匹配的构造函数。因为配置文件中可以通过参数位置索引,也可以通过参数名称来设定参数值,所以还需要解析参数的名称。最后根据解析好的参数名称、参数类型、实际参数就可以确定构造函数,并且将参数转换成对应的类型。 - 构造函数不确定性的验证。 因为有一些构造函数的参数类型为父子关系,所以Spring会做一次验证。
- 如果条件符合(传入参数为空),将解析好的构造函数、参数放入缓存。
- 根据实例化策略通过构造函数、参数实例化bean。
接下来我们看下对象的实例化策略。
对象的实例化主要有两种策略:SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy。其中SimpleInstantiationStrategy通过反射方式创建对象,而CglibSubclassingInstantiationStrategy通过Cglib来创建对象。接下来看下无参实例化的方法:
可以看到无参实例化的方法和带有参数创建bean一样,都是使用InstantiationStrategy来创建bean,但是因为没有参数,所以也就没有必要执行繁琐的确定构造函数的代码,只需要使用无参构造器进行实例化就行了。
到这里我们就把createBeanInstance的逻辑讲完了,同时整个Bean的实例化流程也结束了。到此时,bean已经被实例化了出来,但属性依赖还没有注入,是一个纯净的原生对象。接下来spring会帮我们对bean进行依赖注入及功能增强等等等,那我们就一起进入bean的初始化流程吧!
(2)Bean的初始化
接着上面的逻辑我们继续往下看,来看applyMergedBeanDefinitionPostProcessors方法。
我们可以看到这个函数调用了所有MergedBeanDefinitionPostProcessor的MergedBeanDefinitionPostProcessor方法。MergedBeanDefinitionPostProcessor有很多实现类,其中AutowiredAnnotationBeanPostProcessor是一个非常重要的实现类。主要用来解析所有扫描类里面的@Autowired和@Value注解。其实现的主要方法如下:
AutowiredAnnotationBeanPostProcessor在构造时就已经把@Autowired和@Value注解类加入到了autowiredAnnotationTypes集合中。 AutowiredAnnotationBeanPostProcessor会对bean进行注解扫描,如果扫描到了@Autowired和@Value注解,就会把对应的方法或者属性封装起来,最终封装成InjectionMetadata对象。
看过了applyMergedBeanDefinitionPostProcessors后,我们继续往下看addSingletonFactory方法,这个方法也是用来解决循环依赖问题的,这里的addSingletonFactory会将生产该对象的工厂类singletonFactory放入到名为singletonFactories的HashMap中,而工厂类singletonFactory的函数式方法实现,主要就是调用getEarlyBeanReference(beanName, mbd, bean)。上文提过当对象A和对象B存在循环依赖时,若对象A先开始构建,在进行依赖注入时发现依赖对象B,就去构建对象B,对象B在构建时发现需要注入对象A,此时就会从singletonFactories中获取对象A的工厂singletonFactory实例,然后通过工厂实例获取对象A的引用来完成注入。获取的对象A引用最后会被保存到earlySingletonObjects(HashMap)中。
populateBean
我们接着看populateBean(beanName, mbd, instanceWrapper)方法,这个方法主要进行bean属性的装配工作,即依赖对象的注入。下面的逻辑看着很多,但其实很多逻辑都是为了处理旧版本XML属性配置的,但当我们使用@Autowired时,很多逻辑并不会执行。例如下面的pvs是一个MutablePropertyValues实例,里面实现了PropertyValues接口,提供属性的读写操作实现,同时可以通过调用构造函数实现深拷贝,但这个pvs主要是用来处理XML配置的属性的,当我们使用@Autowired时,其值基本都是null或者为空。(深拷贝:从源对象完美复制出一个相同却与源对象彼此独立的目标对象,这里的相同是指两个对象的状态和动作相同,彼此独立是指改变其中一个对象的状态不会影响到另外一个对象。)
/** * Populate the bean instance in the given BeanWrapper with the property values * from the bean definition. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param bw the BeanWrapper with bean instance */ @SuppressWarnings("deprecation") // for postProcessPropertyValues protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } }
<span class="token comment">// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the</span> <span class="token comment">// state of the bean before properties are set. This can be used, for example,</span> <span class="token comment">// to support styles of field injection.</span> <span class="token keyword">boolean</span> continueWithPropertyPopulation <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment">// 在进行依赖注入前,给InstantiationAwareBeanPostProcessors最后的机会根据用户需求自定义修改Bean的属性值</span> <span class="token comment">// 如果此处返回了false,则后面不会再进行依赖注入。</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isSynthetic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">hasInstantiationAwareBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">for</span> <span class="token punctuation">(</span>BeanPostProcessor bp <span class="token operator">:</span> <span class="token function">getBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bp <span class="token keyword">instanceof</span> <span class="token class-name">InstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> InstantiationAwareBeanPostProcessor ibp <span class="token operator">=</span> <span class="token punctuation">(</span>InstantiationAwareBeanPostProcessor<span class="token punctuation">)</span> bp<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>ibp<span class="token punctuation">.</span><span class="token function">postProcessAfterInstantiation</span><span class="token punctuation">(</span>bw<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> continueWithPropertyPopulation <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>continueWithPropertyPopulation<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// pvs是一个MutablePropertyValues实例,实现了PropertyValues接口,我们配置的对象属性值会被解析到PropertyValues中,</span> <span class="token comment">// 然后通过BeanWrapper进行类型转换并设置给对象,不过PropertyValues主要是用来处理xml配置的属性值的,目前我们基本</span> <span class="token comment">// 使用的都是@Autowired,所以这里的PropertyValues基本都是null。</span> PropertyValues pvs <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">hasPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span> mbd<span class="token punctuation">.</span><span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 这里的AUTOWIRE_BY_NAME、AUTOWIRE_BY_TYPE主要是针对XML配置的依赖bean自动绑定配置,即<bean>的autowire属性。</span> <span class="token comment">// 指的并不是我们使用的@Autowired,@Autowire并不会走下面这段逻辑,所以我们可以暂时不用看下面逻辑。</span> <span class="token keyword">int</span> resolvedAutowireMode <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getResolvedAutowireMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_NAME <span class="token operator">||</span> resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_TYPE<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> MutablePropertyValues newPvs <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MutablePropertyValues</span><span class="token punctuation">(</span>pvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Add property values based on autowire by name if applicable.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_NAME<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">autowireByName</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> newPvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Add property values based on autowire by type if applicable.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_TYPE<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">autowireByType</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> newPvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> pvs <span class="token operator">=</span> newPvs<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">boolean</span> hasInstAwareBpps <span class="token operator">=</span> <span class="token function">hasInstantiationAwareBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 是否进行依赖检查,默认值就是None,所以此处返回false,表示不需要依赖检查</span> <span class="token keyword">boolean</span> needsDepCheck <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getDependencyCheck</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> AbstractBeanDefinition<span class="token punctuation">.</span>DEPENDENCY_CHECK_NONE<span class="token punctuation">)</span><span class="token punctuation">;</span> PropertyDescriptor<span class="token punctuation">[</span><span class="token punctuation">]</span> filteredPds <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>hasInstAwareBpps<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvs <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> pvs <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>BeanPostProcessor bp <span class="token operator">:</span> <span class="token function">getBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bp <span class="token keyword">instanceof</span> <span class="token class-name">InstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> InstantiationAwareBeanPostProcessor ibp <span class="token operator">=</span> <span class="token punctuation">(</span>InstantiationAwareBeanPostProcessor<span class="token punctuation">)</span> bp<span class="token punctuation">;</span> 调用了InstantiationAwareBeanPostProcessor#postProcessPropertyValues方法, <span class="token comment">// 调用InstantiationAwareBeanPostProcessor#postProcessPropertyValues方法,该方法有两个重要的实现:</span> <span class="token comment">// AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor,前者会通过该方法注入</span> <span class="token comment">// 所有@Autowired注解的对象,而后者通过该方法注入所有@Resource注解的对象。</span> PropertyValues pvsToUse <span class="token operator">=</span> ibp<span class="token punctuation">.</span><span class="token function">postProcessProperties</span><span class="token punctuation">(</span>pvs<span class="token punctuation">,</span> bw<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvsToUse <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>filteredPds <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> filteredPds <span class="token operator">=</span> <span class="token function">filterPropertyDescriptorsForDependencyCheck</span><span class="token punctuation">(</span>bw<span class="token punctuation">,</span> mbd<span class="token punctuation">.</span>allowCaching<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> pvsToUse <span class="token operator">=</span> ibp<span class="token punctuation">.</span><span class="token function">postProcessPropertyValues</span><span class="token punctuation">(</span>pvs<span class="token punctuation">,</span> filteredPds<span class="token punctuation">,</span> bw<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvsToUse <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> pvs <span class="token operator">=</span> pvsToUse<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>needsDepCheck<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>filteredPds <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> filteredPds <span class="token operator">=</span> <span class="token function">filterPropertyDescriptorsForDependencyCheck</span><span class="token punctuation">(</span>bw<span class="token punctuation">,</span> mbd<span class="token punctuation">.</span>allowCaching<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">checkDependencies</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> filteredPds<span class="token punctuation">,</span> pvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 将pvs上所有的属性填充到BeanWrapper对应的Bean实例中,前面说过了这里主要是用来填充XML配置的属性,</span> <span class="token comment">// 所以若我们使用@Autowired时pvs为null,这里并不会执行。</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvs <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">applyPropertyValues</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> pvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
我们接下来看postProcessPropertyValues方法,@Autowired和@Resource注解对象的注入就是通过该方法实现的,上面其实已经提过了他们的实现类分别为AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor。两者的实现逻辑基本相同,我们这里仅介绍@Autowired的实现。
对象中@Autowired 注入点(字段或方法)都会被提前解析成元信息并保存到InjectionMetadata中。我们可以看到这里的逻辑:依次遍历所有的注入点元信息 InjectedElement,进行属性注入。(下图中的checkedElements主要是为了防止重复注入)
我们继续跟进element.inject(target, beanName, pvs)方法,这个方法有两个实现:AutowiredFieldElement和AutowiredMethodElement,他们分别用于处理属性注入和方法注入,我们这里只看AutowiredFieldElement,两者实现逻辑基本相同。
我们可以看到主体逻辑其实很简单:首先解析依赖的属性对象,然后通过反射设置属性。其中beanFactory.resolveDependency()是Spring 进行依赖查找的核心 API。
beanFactory.resolveDependency()的实际功能由doResolveDependency()函数实现,其主要功能为:
- 快速查找: @Autowired 注解处理场景。AutowiredAnnotationBeanPostProcessor 处理 @Autowired 注解时,如果注入的对象只有一个,会将该 bean 对应的名称缓存起来,下次直接通过名称查找会快很多。
- 注入指定值:@Value 注解处理场景。QualifierAnnotationAutowireCandidateResolver 处理 @Value 注解时,会读取 @Value 对应的值进行注入。如果是 String 要经过三个过程:①占位符处理 -> ②EL 表达式解析 -> ③类型转换,这也是一般的处理过程,BeanDefinitionValueResolver 处理 String 对象也是这个过程。
- 集合依赖查询:直接全部委托给 resolveMultipleBeans 方法。
- 单个依赖查询:先调用 findAutowireCandidates 查找所有可用的依赖,如果有多个依赖,则根据规则匹配: @Primary -> @Priority -> ③方法名称或字段名称。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint <span class="token operator">=</span> ConstructorResolver<span class="token punctuation">.</span><span class="token function">setCurrentInjectionPoint</span><span class="token punctuation">(</span>descriptor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 1. 快速查找,根据名称查找。AutowiredAnnotationBeanPostProcessor用到</span> Object shortcut <span class="token operator">=</span> descriptor<span class="token punctuation">.</span><span class="token function">resolveShortcut</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>shortcut <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> shortcut<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 2. 注入指定值,QualifierAnnotationAutowireCandidateResolver解析@Value会用到</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> type <span class="token operator">=</span> descriptor<span class="token punctuation">.</span><span class="token function">getDependencyType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Object value <span class="token operator">=</span> <span class="token function">getAutowireCandidateResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSuggestedValue</span><span class="token punctuation">(</span>descriptor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token keyword">instanceof</span> <span class="token class-name">String</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 2.1 占位符解析</span> String strVal <span class="token operator">=</span> <span class="token function">resolveEmbeddedValue</span><span class="token punctuation">(</span><span class="token punctuation">(</span>String<span class="token punctuation">)</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> BeanDefinition bd <span class="token operator">=</span> <span class="token punctuation">(</span>beanName <span class="token operator">!=</span> null <span class="token operator">&&</span> <span class="token function">containsBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token function">getMergedBeanDefinition</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span> <span class="token operator">:</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2.2 Spring EL 表达式</span> value <span class="token operator">=</span> <span class="token function">evaluateBeanDefinitionString</span><span class="token punctuation">(</span>strVal<span class="token punctuation">,</span> bd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> TypeConverter converter <span class="token operator">=</span> <span class="token punctuation">(</span>typeConverter <span class="token operator">!=</span> null <span class="token operator">?</span> typeConverter <span class="token operator">:</span> <span class="token function">getTypeConverter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 2.3 类型转换</span> <span class="token keyword">return</span> converter<span class="token punctuation">.</span><span class="token function">convertIfNecessary</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> type<span class="token punctuation">,</span> descriptor<span class="token punctuation">.</span><span class="token function">getTypeDescriptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">UnsupportedOperationException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token punctuation">(</span>descriptor<span class="token punctuation">.</span><span class="token function">getField</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> null <span class="token operator">?</span> converter<span class="token punctuation">.</span><span class="token function">convertIfNecessary</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> type<span class="token punctuation">,</span> descriptor<span class="token punctuation">.</span><span class="token function">getField</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">:</span> con