Spring源码之bean的实例化createBeanInstance方法解读
目录
- 前言
- 示例代码
- `Java` 实体
- `xml` 配置文件
- 测试类
- `createBeanInstance()` 方法概述
- `obtainFromSupplier()` 通过 `Supplier` 实例化
- `instantiateUsingFactoryMethod()` 通过工厂方法实例化
- 通过静态工厂实例化 `bean` 示例
- 通过实例工厂实例化 `bean` 示例
- `@Bean` 方式实例化 `bean` 示例
- `autowireConstructor()` 用合适的构造函数实例化
- 此处可以使用示例代码进行调试
- 给 `createBeanInstance()` 方法打上断点
- 使用 `F8` 键调试
- 按功能划分 `autowireConstructor()` 方法可分为四步
- 解析构造函数参数
- 获取候选的构造函数列表
- 解析构造函数参数个数
- 寻找最匹配的构造函数
- `instantiateBean()` 用无参构造函数实例化
- 此处也可以使用示例代码进行调试
- 实体类更改如下
- 给 `createBeanInstance()` 方法打上断点
- 使用 `F8` 键调试
- `createBeanInstance()` 方法小结
前言
这篇文章是 IOC
容器初始化启动时,抽象类 AbstractAutowireCapableBeanFactory
的 doCreateBean()
方法里面的 createBeanInstance()
方法,顾名思义是 bean
的实例化方法
阅读本篇文章,同时可以参考阅读 spring源码之getBean(获取 bean)方法解读(二) 和 Spring Aop代理对象的产生(一) 这两篇文章的 doCreateBean()
方法
示例代码
我们可以使用示例代码进行调试,以便更容易理解源码
Java
实体
public class Student {private String name;public Student() {System.out.println("Student 的无参构造器被调用了");}public Student(String name) {System.out.println("Student 的有参构造器被调用了");this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}
xml
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="student" class="com.atguigu.pojo.Student"><!--构造函数注入(实体类不需要 set/get 方法)--><constructor-arg value="小明"></constructor-arg></bean>
</beans>
测试类
public class Test2 {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-spring.xml");Student student = (Student) applicationContext.getBean("student");System.out.println(student.toString());}
}测试结果:
Student 的有参构造器被调用了
Student{name='小明'}
createBeanInstance()
方法概述
createBeanInstance()
方法是 spring
实例化 bean
的核心代码,它根据不同的情况会调用四种实例化方法
obtainFromSupplier()
:通过Supplier
实例化instantiateUsingFactoryMethod()
:通过工厂方法实例化autowireConstructor()
:用合适的构造函数实例化instantiateBean()
:用无参构造函数实例化
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 确认需要创建的bean实例的类可以实例化Class<?> beanClass = resolveBeanClass(mbd, beanName);// 确保class不为空,并且访问权限是publicif (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());}/*** ----------1,通过Supplier实例化------------*/Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}/*** ----------2,通过工厂方法实例化------------*/if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}/*** ----------3,用合适的构造函数实例化------------** 一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器。* 在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中,* 避免再次创建相同bean时再次解析*/// 标记下,防止重复创建同一个beanboolean resolved = false;// 是否需要自动装配,构造有参数的需要boolean autowireNecessary = false;// 如果没有参数if (args == null) {synchronized (mbd.constructorArgumentLock) {// 一个类中有多个构造函数,每个构造函数都有不同的参数,所以调用前需要先根据参数锁定构造函数或对应的工厂方法if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}// 有构造参数的或者工厂方法if (resolved) {// 构造器有参数if (autowireNecessary) {// 构造函数自动注入return autowireConstructor(beanName, mbd, null, null);}else {// 使用默认构造函数构造return instantiateBean(beanName, mbd);}}// 从bean后置处理器中为自动装配寻找构造方法Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// 找出最合适的默认构造方法ctors = mbd.getPreferredConstructors();if (ctors != null) {// 构造函数自动注入return autowireConstructor(beanName, mbd, ctors, null);}/*** ----------4,使用默认构造函数构造------------*/return instantiateBean(beanName, mbd);
}
以上源码的执行步骤流程
- 如果
RootBeanDefinition
中存在Supplier
接口实例,则使用Supplier
接口回调来实例化 - 如果
RootBeanDefinition
中存在factoryMethodName
属性,或者在配置文件中配置了factory-method
标签,spring
会尝试使用instantiateUsingFactoryMethod()
方法,根据RootBeanDefinition
中的配置生成bean
的实例。如果一个类中的方法被@Bean
注解修饰,那么spring
则会将其封装成一个ConfigurationClassBeanDefinition
。此时factoryMethodName
也被赋值。所以也会调用instantiateUsingFactoryMethod()
方法通过反射完成方法的调用,并将结果注入spring
容器中 - 当以上两种都没有配置时,
spring
则打算通过构造函数来实例化bean
。首先会判断是否有缓存,即构造函数是否已经被解析过了, 因为一个bean
可能会存在多个构造函数,这时候spring
会根据参数列表的来判断使用哪个构造函数进行实例化。但是判断过程比较消耗性能,所以spring
将判断好的构造函数缓存到RootBeanDefinition
中的resolvedConstructorOrFactoryMethod
属性中 - 如果缓存,则不需要解析,直接调用
autowireConstructor()
或者instantiateBean()
方法实例化bean
。有参构造调用autowireConstructor()
方法,无参构造调用instantiateBean()
方法 - 否则需要先进行解析,这里通过
determineConstructorsFromBeanPostProcessors()
方法调用了SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors()
的后处理器方法来进行解析,spring
默认的实现在AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors()
方法中 - 获取解析后的候选的构造函数列表
ctors
后(最终的构造函数就从这个列表中选取),开始调用autowireConstructor()
或者instantiateBean()
方法实例化bean
。在autowireConstructor()
方法中,进行了候选构造函数的选举,选择最合适的构造函数来构建bean
,如果缓存已解析的构造函数,则不用选举,直接使用解析好的构造来进行bean
的实例化
obtainFromSupplier()
通过 Supplier
实例化
Supplier
方式比较简单,在此不再赘述,instanceSupplier.get()
回调到自己定义的函数里面返回一个实例对象然后包装成 BeanWrapperImpl
返回就行了
instantiateUsingFactoryMethod()
通过工厂方法实例化
如果 RootBeanDefinition
类中存在 factoryMethodName
属性,或者在配置文件中配置了 factory-method
或 factory-bean
标签,spring
会尝试使用 instantiateUsingFactoryMethod()
方法进行 bean
的实例化
这个源码太长,也并不重要,就不在这里展示了。简单来说,这里可以分为两种情况
- 在
xml
文件配置中,可以使用factory-bean
和factory-method
两个标签可以指定一个类中的方法,spring
会将这个指定的方法的返回值作为bean
返回(如果方法是静态方法,则可以不创建factory-bean
就直接调用,否则需要先将factory-bean
注入到spring
容器中) - 对
@Bean
注解的解析。在ConfigurationClassPostProcessor
后处理器中,会对被@Bean
注解修饰的方法进行解析,生成一个ConfigurationClassBeanDefinition
的BeanDefinition
。此时BeanDefinition
的factoryMethodName
正是@Bean
修饰的方法本身。所以这里会调用instantiateUsingFactoryMethod()
方法。通过回调的方式调用@Bean
修饰的方法,并将返回结果注入到spring
容器中
通过静态工厂实例化 bean
示例
public class UserFactory {// 静态方法public static User getUser1() {return new User();}
}
xml
配置文件
<!-- 使用静态工厂实例化 user -->
<bean id="user1" class="ioc.service.UserFactory" factory-method="getUser1"></bean>
通过实例工厂实例化 bean
示例
public class UserFactory {// 普通方法public User getUser2() {return new User();}
}
xml
配置文件
<!-- 使用实例工厂实例化 user -->
<bean id="userFactory" class="ioc.service.UserFactory"></bean>
<bean id="user2" factory-bean="userFactory" factory-method="getUser2"></bean>
@Bean
方式实例化 bean
示例
@Bean
public ShiroDialect getShiroDialect() {return new ShiroDialect();
}
autowireConstructor()
用合适的构造函数实例化
简单来说,就是根据传入的参数列表,来匹配合适的有参构造器进行 bean
的实例化
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {// 实例化BeanWrapper。是包装bean的容器BeanWrapperImpl bw = new BeanWrapperImpl();this.beanFactory.initBeanWrapper(bw);Constructor<?> constructorToUse = null;ArgumentsHolder argsHolderToUse = null;Object[] argsToUse = null;// explicitArgs通过getBean方法传入// 如果getBean方法调用的时候指定方法参数那么直接使用if (explicitArgs != null) {argsToUse = explicitArgs;}else {// 如果在调用getBean方法的时候没有指定,则尝试从配置文件中解析Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {// 尝试从缓存中获取constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;if (constructorToUse != null && mbd.constructorArgumentsResolved) {// Found a cached constructor...// 从缓存中找到了构造器,那么继续从缓存中寻找缓存的构造器参数argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 没有缓存的参数,就需要获取配置i文件中配置的参数argsToResolve = mbd.preparedConstructorArguments;}}}// 如果缓存中没有缓存的参数的话,即argsToResolve不为空,就需要解析配置的参数if (argsToResolve != null) {// 解析参数类型,比如将配置的String类型转换为list、boolean等类型argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);}}// 如果没有缓存,就需要从构造函数开始解析if (constructorToUse == null || argsToUse == null) {// Take specified constructors, if any.// 如果传入的构造器数组不为空,就使用传入的过后早期参数,否则通过反射获取class中定义的构造器Constructor<?>[] candidates = chosenCtors;if (candidates == null) {Class<?> beanClass = mbd.getBeanClass();try {// 使用public的构造器或者所有构造器candidates = (mbd.isNonPublicAccessAllowed() ?beanClass.getDeclaredConstructors() : beanClass.getConstructors());}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Resolution of declared constructors on bean Class [" + beanClass.getName() +"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);}}if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {Constructor<?> uniqueCandidate = candidates[0];if (uniqueCandidate.getParameterCount() == 0) {synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;mbd.constructorArgumentsResolved = true;mbd.resolvedConstructorArguments = EMPTY_ARGS;}bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));return bw;}}// Need to resolve the constructor.boolean autowiring = (chosenCtors != null ||mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);ConstructorArgumentValues resolvedValues = null;int minNrOfArgs;if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;}else {// 提取配置文件中的配置的构造函数参数ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();// 用于承载解析后的构造函数参数的值resolvedValues = new ConstructorArgumentValues();// 能解析到的参数个数minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}// 排序给定的构造函数,public的构造函数优先,参数数量降序AutowireUtils.sortConstructors(candidates);int minTypeDiffWeight = Integer.MAX_VALUE;Set<Constructor<?>> ambiguousConstructors = null;LinkedList<UnsatisfiedDependencyException> causes = null;for (Constructor<?> candidate : candidates) {int parameterCount = candidate.getParameterCount();// 如果已经找到选用的构造函数或者需要的参数个数小于当前的构造函数参数个数则终止,前面已经经过了排序操作if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {// Already found greedy constructor that can be satisfied ->// do not look any further, there are only less greedy constructors left.break;}if (parameterCount < minNrOfArgs) {// 参数个数不相等continue;}ArgumentsHolder argsHolder;Class<?>[] paramTypes = candidate.getParameterTypes();if (resolvedValues != null) {// 有参数则根据值构造对应参数类型的参数try {String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);if (paramNames == null) {// 获取参数名称探索器ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {// 获取指定构造函数的参数名称paramNames = pnd.getParameterNames(candidate);}}// 根据名称和数据类型创建参数持有者argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);}catch (UnsatisfiedDependencyException ex) {if (logger.isTraceEnabled()) {logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);}// Swallow and try next constructor.if (causes == null) {causes = new LinkedList<>();}causes.add(ex);continue;}}else {// Explicit arguments given -> arguments length must match exactly.if (parameterCount != explicitArgs.length) {continue;}// 构造函数没有参数的情况argsHolder = new ArgumentsHolder(explicitArgs);}// 探测是否有不确定性的构造函数存在,例如不同构造函数的参数为父子关系int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this constructor if it represents the closest match.// 如果它代表着当前最接近的匹配则选择作为构造函数if (typeDiffWeight < minTypeDiffWeight) {constructorToUse = candidate;argsHolderToUse = argsHolder;argsToUse = argsHolder.arguments;minTypeDiffWeight = typeDiffWeight;ambiguousConstructors = null;}else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {if (ambiguousConstructors == null) {ambiguousConstructors = new LinkedHashSet<>();ambiguousConstructors.add(constructorToUse);}ambiguousConstructors.add(candidate);}}if (constructorToUse == null) {if (causes != null) {UnsatisfiedDependencyException ex = causes.removeLast();for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}throw ex;}throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Could not resolve matching constructor " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");}else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous constructor matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousConstructors);}if (explicitArgs == null && argsHolderToUse != null) {// 将解析的构造函数加入缓存argsHolderToUse.storeCache(mbd, constructorToUse);}}Assert.state(argsToUse != null, "Unresolved constructor arguments");// 将构造的实例加入BeanWrapper中bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));return bw;
}
以上源码的执行步骤流程
- 首先判断
explicitArgs
是否为空,如果不为空,则就直接使用explicitArgs
作为构造函数的参数。explicitArgs
代表的意思是调用getBean()
方法是的传参,如下
- 如果
explicitArgs
为空,则尝试从缓存中获取,也即是从RootBeanDefinition
的resolvedConstructorArguments
属性或preparedConstructorArguments
属性中获取。resolvedConstructorArguments
代表完全解析好的参数;preparedConstructorArguments
代表尚未完全解析的参数,如果获取到preparedConstructorArguments
,则需要在进一步的解析 - 如果缓存中也没有获取到,则只能自己开始分析来获取候选构造函数列表,关于候选构造函数的信息,在调用该方法时就已经传递了过来,即
Constructor<?>[] chosenCtors
,如果Constructor<?>[] chosenCtors
为null
,则通过反射获取候选构造函数列表candidates
- 获取到候选构造函数列表
candidates
后,则会优先判断获取到的candidates
是否只有一个构造函数,如果只要一个,则不需要解析,直接保存相关信息即解析完毕 - 否则,进行候选构造函数列表
candidates
的选举,寻找最合适的构造函数,对candidates
按照public
构造函数优先参数数量降序,非public
构造函数参数数量降序规则排序,目的是为了后面检索的时候可以更快速判断是否有合适的构造函数 - 排序结束后,开始遍历构造函数,按照构造函数的参数类型和数量与构造函数一一匹配,寻找差异性最小的构造函数作为最终的构造函数并通过
cglib
或者反射来实例化bean
此处可以使用示例代码进行调试
给 createBeanInstance()
方法打上断点
使用 F8
键调试
当下面的代码
return autowireConstructor(beanName, mbd, ctors, args);
执行完毕时,随即该 createBeanInstance()
方法亦执行完毕。此时可以发现
说明:该 student
这个 bean
用合适的构造函数实例化成功了(使用有参构造器),此时无需再使用无参构造器来进行实例化了,故而没有调用无参构造器
按功能划分 autowireConstructor()
方法可分为四步
- 解析构造函数参数
- 获取候选的构造函数列表
- 解析构造函数参数个数
- 寻找最匹配的构造函数
解析构造函数参数
// explicitArgs通过getBean方法传入
// 如果getBean方法调用的时候指定方法参数那么直接使用
if (explicitArgs != null) {argsToUse = explicitArgs;
}
else {// 如果在调用getBean方法的时候没有指定,则尝试从配置文件中解析Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {// 尝试从缓存中获取constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;if (constructorToUse != null && mbd.constructorArgumentsResolved) {// Found a cached constructor...// 从缓存中找到了构造器,那么继续从缓存中寻找缓存的构造器参数argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 没有缓存的参数,就需要获取配置i文件中配置的参数argsToResolve = mbd.preparedConstructorArguments;}}}// 如果缓存中没有缓存的参数的话,即argsToResolve不为空,就需要解析配置的参数if (argsToResolve != null) {// 解析参数类型,比如将配置的String类型转换为list、boolean等类型argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);}
}
- 如果有传入参数
explicitArgs
,则直接使用explicitArgs
- 如果没有传入,尝试从缓存中获取
- 如果参数完全解析了,则直接使用,如果没有则调用
resolvePreparedArguments()
方法进行解析
这里解释一下 resolvePreparedArguments()
方法的作用。我们声明的构造函数的可能是这样的
public ConstructorDemoA(Integer name) {this.name = name;
}
但是我们在配置的时候配置文件却是这样的
<bean id="constructorDemoA" class="com.kingfish.springbootdemo.constructor.ConstructorDemoA"><constructor-arg index="0" value="666" ></constructor-arg>
</bean>
这时候,spring
就需要有一个过程,从 spring
的 “666”
到 Integer
的 666
的转变,这个方法就是做类型转化的工作。但需要注意调用这个方法的前提条件是 argsToResolve != null
获取候选的构造函数列表
// 如果传入的构造器数组不为空,就使用传入的过后早期参数,否则通过反射获取class中定义的构造器
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {Class<?> beanClass = mbd.getBeanClass();try {// 获取bean的构造函数candidates = (mbd.isNonPublicAccessAllowed() ?beanClass.getDeclaredConstructors() : beanClass.getConstructors());}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Resolution of declared constructors on bean Class [" + beanClass.getName() +"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);}
}// 如果构造函数只有一个 & getBean 没有传参 & 构造参数无参
// 满足上述三个条件,则无需继续筛选,直接创建 BeanWrapper 并返回即可。
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {Constructor<?> uniqueCandidate = candidates[0];if (uniqueCandidate.getParameterCount() == 0) {synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;mbd.constructorArgumentsResolved = true;mbd.resolvedConstructorArguments = EMPTY_ARGS;}bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));return bw;}
}
- 外部是否传入了候选构造函数列表(
chosenCtors == null
) - 如果没传入(
chosenCtors
为null
),通过反射获取构造函数列表 - 如果构造函数只有一个
& getBean
没有传参&
构造参数无参,则直接使用这唯一一个构造函数并返回
这里需要注意点是 传入的 chosenCtors
,在不同的调用场景下可能会传入null
,或者调用 SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors()
方法返回的值。spring
默认的实现是在 AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors()
方法中
解析构造函数参数个数
// 解析出来的构造函数的个数
int minNrOfArgs;
// 如果explicitArgs不为空,直接使用它作为参数,毕竟是传入的参数,没必要再从进一步解析
if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;
}
else {// 提取配置文件中的配置的构造函数参数ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();// 用于保存解析后的构造函数参数的值,在resolveConstructorArguments中可以看到他的作用resolvedValues = new ConstructorArgumentValues();// 最终解析到的构造函数参数个数minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
在 spring
中指定的构造函数会保存在 RootBeanDefinition.constructorArgumentValues
中,类型为 ConstructorArgumentValues
如下,可以看到 ConstructorArgumentValues
分为两部分保存参数
public class ConstructorArgumentValues {// 按照顺序声明的参数列表private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>();// 按照类型声明的参数列表private final List<ValueHolder> genericArgumentValues = new ArrayList<>();...
}
如下的 xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="constructorDemoA" class="com.kingfish.springbootdemo.constructor.ConstructorDemoA"><constructor-arg index="0" ref="constructorDemoB"></constructor-arg><constructor-arg index="1" value="666" ></constructor-arg><constructor-arg value="999" ></constructor-arg></bean><bean id="constructorDemoB" class="com.kingfish.springbootdemo.constructor.ConstructorDemoB"></bean>
</beans>
constructorDemoB,666
就被保存到 indexedArgumentValues
中, 999
就被保存到 genericArgumentValues
中
寻找最匹配的构造函数
...// 排序构造函数,方便后面检索AutowireUtils.sortConstructors(candidates);// 差异度,最后选择minTypeDiffWeight 最小的作为最匹配的构造函数int minTypeDiffWeight = Integer.MAX_VALUE;Set<Constructor<?>> ambiguousConstructors = null;LinkedList<UnsatisfiedDependencyException> causes = null;// 筛选构造函数,根据参数数量,参数类型匹配for (Constructor<?> candidate : candidates) {... if (resolvedValues != null) {try {// 获取参数名String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);if (paramNames == null) {ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {paramNames = pnd.getParameterNames(candidate);}}argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);}catch (UnsatisfiedDependencyException ex) {if (logger.isTraceEnabled()) {logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);}// Swallow and try next constructor.if (causes == null) {causes = new LinkedList<>();}causes.add(ex);continue;}}else {// Explicit arguments given -> arguments length must match exactly.if (parameterCount != explicitArgs.length) {continue;}argsHolder = new ArgumentsHolder(explicitArgs);}...if (explicitArgs == null && argsHolderToUse != null) {// 将解析出来的信息缓存到RootBeanDefinition中argsHolderToUse.storeCache(mbd, constructorToUse);}}
Assert.state(argsToUse != null, "Unresolved constructor arguments");
// 创建bean,并保存
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
这一步的目的就是根据参数数量和参数列表来选择最合适的构造函数,并且调用 instantiate(beanName, mbd, constructorToUse, argsToUse)
方法来进行 bean
实例化
- 由于在配置文件中声明
bean
不仅仅可以使用参数位置索引的方式创建,也支持通过参数名称设定参数值的情况,如下
<constructor-arg name="constructorDemoB" ref="constructorDemoB"></constructor-arg>
所以这时候,就需要首先确定构造函数中的参数名称。而获取参数名的方式有两种,一种是通过注解直接获取(@ConstructorProperties
注解获取),即上面代码中对应的 ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
,另一种是通过 spring 同构的工具类 ParameterNameDiscoverer
,这个在代码中也有使用。完成这一步的时候,构造函数、参数名称、参数类型、参数值都确定后就可以锁定构造函数以及转换对应的参数类型了
instantiate()
方法也很简单,根据beanFactory
中的bean
实例化策略来实例化对象
private Object instantiate(String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse) {try {// 获取实例化策略InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();// 通过策略实例化beanif (System.getSecurityManager() != null) {return AccessController.doPrivileged((PrivilegedAction<Object>) () ->strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),this.beanFactory.getAccessControlContext());}else {return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);}}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean instantiation via constructor failed", ex);}
}
关于实例化策略,主要两种 SimpleInstantiationStrategy
和 CglibSubclassingInstantiationStrategy
,简单实例化策略(直接反射)和 Cglib
动态代理策略,默认第二种
instantiateBean()
用无参构造函数实例化
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {try {Object beanInstance;if (System.getSecurityManager() != null) {beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),getAccessControlContext());}else {// 获取实例化策略并且进行实例化操作beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);}// 包装成BeanWrapperBeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);}
}
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// Don't override the class with CGLIB if no overrides.// bd对象定义中,是否包含MethodOverride列表,spring中有两个标签参数会产生MethodOverrides,分别是lookup-method,replaced-method// 没有MethodOverrides对象,可以直接实例化if (!bd.hasMethodOverrides()) {// 实例化对象的构造方法Constructor<?> constructorToUse;// 锁定对象,使获得实例化构造方法线程安全synchronized (bd.constructorArgumentLock) {// 查看bd对象里使用否含有构造方法constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;// 如果没有if (constructorToUse == null) {// 从bd中获取beanClassfinal Class<?> clazz = bd.getBeanClass();// 如果要实例化的beanDefinition是一个接口,则直接抛出异常if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {// 获取系统安全管理器if (System.getSecurityManager() != null) {constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);}else {// 获取默认的午餐构造器constructorToUse = clazz.getDeclaredConstructor();}// 获取到构造器之后将构造器赋值给bd中的属性bd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}// 通过反射生成具体的实例化对象return BeanUtils.instantiateClass(constructorToUse);}else {// Must generate CGLIB subclass.// 必须生成cglib子类return instantiateWithMethodInjection(bd, beanName, owner);}
}
getDeclaredConstructor(Class<?>... parameterTypes)
: 这种方法会返回指定參数类型的全部构造器,包含public
的和非public
的,当然也包含private
的getDeclaredConstructors()
: 的返回结果就没有參数类型的过滤了getConstructor(Class<?>... parameterTypes)
: 这种方法返回的是上面那个方法返回结果的子集。仅仅返回指定參数类型訪问权限是public
的构造器getConstructors()
: 的返回结果相同也没有參数类型的过滤
这里的逻辑一句话概括 : 是否有方法被覆盖(是否使用 replace
或 lookup
进行配置),有则使用 cglib
动态代理,增强方法,否则直接通过反射实例化 bean
。这一块判断方法是否被重写,不是为了事务或者 aop
,因为解析还没到那一步,这里是为了 lookup-method
和 replaced-method
标签
此处也可以使用示例代码进行调试
实体类更改如下
public class Student {private String name;public Student() {System.out.println("Student 的无参构造器被调用了");}public Student(String name) {System.out.println("Student 的有参构造器被调用了");this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}
给 createBeanInstance()
方法打上断点
使用 F8
键调试
当下面的代码
return instantiateBean(beanName, mbd);
执行完毕时,随即该 createBeanInstance()
方法亦执行完毕。此时可以发现
说明:该 student
这个 bean
使用无参构造器实例化已成功了,由于是无参构造器故而属性为 null
,此时也未调用属性填充注入的 populateBean()
方法,所以为 null
createBeanInstance()
方法小结
该方法处于 spring
创建 bean
的入口阶段,会使用各种方法来尝试完成 bean
的实例化。对于具体使用那种方式进行实例化,spring
会使用类 RootBeanDefinition
去解析 xml
和注解类型的配置文件
- 第一步:尝试使用
Supplier
方式实例化 - 第二步:尝试使用工厂方法实例化;会使用类
RootBeanDefinition
去解析factory-method,factory-bean
标签和@Bean
注解,如果没有解析到,就不会使用这种方式实例化 - 第三步:尝试使用用合适的有参构造器实例化;会使用类
RootBeanDefinition
去解析构造器参数,找寻最合适的有参构造器用来实例化;如果没有,就不会使用这种方式实例化 - 最后:使用无参构造器实例化
但是并未完成属性填充、接口特性实现(如 Aware
)、标签设置(如 inti-method
)的设置。在后续的 AbstractAutowireCapableBeanFactory#populateBean()
方法中完成了属性的填充
Spring源码之bean的实例化createBeanInstance方法解读相关推荐
- Spring源码解析-bean实例化
Spring源码解析-bean实例化 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean. 1. Bean实例化源码 虽然实例化Bean有多种方式(包括静态工厂和工厂实例 ...
- Spring 源码解析 - Bean创建过程 以及 解决循环依赖
一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...
- Spring源码深度解析,Spring源码以及Bean的生命周期(五)(附代码示例:)
五)Bean 的生命周期,创建---初始化---销毁的过程 目录 五)Bean 的生命周期,创建---初始化---销毁的过程 一 , 指定初始化方法 init-method 方法 二 ,指定销毁 ...
- Spring源码剖析——Bean的配置与启动
IOC介绍 相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Sp ...
- Spring源码之Bean的注册(使用XML配置的方式)
本文分析的Spring源码是5.2.2版本,使用Gradle进行管理. 一.Bean的注册,先来看通过XML配置Bean的方式 1.配置applicationContext.xml: <?xml ...
- 一、如何阅读Spring源码(全网最简单的方法)
学习Java最好最有效的方法是学习Spring,但是最笨最没效的方法也是学习Spring. 为什么这么说呢?道理其实很简单 A.Spring很庞大,很完善,也非常的底层,如果我们学会的Spring,那 ...
- Spring源码分析——Bean的生命周期
文章目录 说明 测试代码 说明 本文从源码的角度分析Spring中Bean的加载过程,本文使用的Spring版本为4.3.25.RELEASE 测试代码 测试代码如下,根据这段简单的测试代码,一步步跟 ...
- spring源码之bean加载(bean解析下篇)
bean的加载步骤: MyTestBean bean = (MyTestBean) bf.getBean("myTestBean"); 步骤: (1) 转换对应的beanName ...
- 三 spring源码解析--- Bean解析接口结构分析
2019独角兽企业重金招聘Python工程师标准>>> 解析Bean是通过定义抽象的bean reader来解析,结构图如下 1.AbstractBeanDefinitionRead ...
- Spring源码之Bean的注册(注解方式)
1.创建AnnotationConfigApplicationContext AnnotationConfigApplicationContext context = new AnnotationCo ...
最新文章
- 美国物理超级计算机,美国科学家在物理学的一个分支领域朝着开发超级计算机迈进了一步。这一分支领域研究的是人眼看不见的粒子。...
- etcd集群搭建(高可用)
- matlab中图像太大,图像处理:算法在MATLAB中耗时太长
- SSM框架中分页插件pageHelper的使用实例
- java定义商品金额_老大说:谁要再用double定义商品金额,就自己收拾东西走
- spring.profiles.active和spring.profiles.include的使用与区别
- [转载] ubuntu下pip的安装、升级和使用
- geotools将shp数据存入postgres
- 科学与星球大战:当科幻遇到现实
- 机器人学——机器人导航
- ESP32-C3入门教程 基础篇(八、NVS — 非易失性存储库的使用)
- Linux虚拟机基本操作、Xshell连接、更改IP
- 微信小程序--震动反馈
- android应用中为按键添加声音
- 图像彩色化方法(深度学习)
- c11计算机考试,2015年计算机二级考试C++备考练习试题及答案(6)
- CLIP 改进工作串讲(下)
- javascript查找并输出英语文章出现最多次数单词,和英语字符中出现最多字母
- 【ESP32】11.雨滴探测实验(ADC)
- MIT photonic band 安装
热门文章
- 容器技术Docker K8s 9 容器服务ACK应用场景
- 2021-08-27最大回文子串 leetcode
- pycharm关闭pytest模式
- ModuleNotFoundError: No module named 'exceptions'
- MIMO-OTFS in High-Doppler Fading Channels:Signal Detection and Channel Estimation(2)
- python从键盘输入一个字符串、将小写字母_# 每日一道面试题 # 从键盘输入一个字符串,将小写字母全部转换成大写字母,然后输出到一个磁盘文件test中保存。...
- 西门子S7系列中间人攻击:流量劫持和转发(一)
- sql报错SELECT list is not in GROUP BY clause and contains nonaggregated column
- 爬虫爬取免费代理ip,验证代理ip有效性,保存到本地txt,建立代理池
- 关于Redis启动时报权限不够(-bash: /usr/local/bin/redis-server: Permission denied)