上一篇笔者漏掉了一块内容,关于registerDisposableBeanIfNecessary,这个方法的主要作用是向Spring容器注册销毁的Bean,这个作用是干啥呢?其实也简单,Spring容器关闭后,将会调用这些Bean进行销毁操作,关于这块咱们后续再详细分析。
接着上一篇幅,咱们讲讲createBeanInstance这个方法,这个方法是创建(也就是new)实例用的,我们说过,在创建之前得确定要使用的构造函数,那么Spring是怎么知道使用哪个构造函数的呢?上代码:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//关键点一
Class<?> beanClass = resolveBeanClass(mbd, beanName);
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<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
//关键点三
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
boolean 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);
}
}
// 关键点四
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);
}
return instantiateBean(beanName, mbd);
}
关键点一
Class<?> beanClass = resolveBeanClass(mbd, beanName)这个方法表示从beanDefinition中得到Class实例,代码如下:
protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
throws CannotLoadBeanClassException {
try {
if (mbd.hasBeanClass()) {
return mbd.getBeanClass();
}
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}else {
return doResolveBeanClass(mbd, typesToMatch);
}
}
......
}
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
throws ClassNotFoundException {
ClassLoader beanClassLoader = getBeanClassLoader();
ClassLoader dynamicLoader = beanClassLoader;
boolean freshResolve = false;
......
String className = mbd.getBeanClassName();
if (className != null) {
Object evaluated = evaluateBeanDefinitionString(className, mbd);
if (!className.equals(evaluated)) {
if (evaluated instanceof Class) {
return (Class<?>) evaluated;
}else if (evaluated instanceof String) {
className = (String) evaluated;
freshResolve = true;
}else {
throw new IllegalStateException("Invalid class name expression result: " + evaluated);
}
}
if (freshResolve) {
......
return ClassUtils.forName(className, dynamicLoader);
}
}
return mbd.resolveBeanClass(beanClassLoader);
}
为啥要通过一个单独的方法获取到这个Class实例呢?朋友们应该清楚,Spring扫描后存放在BeanDefinition中的是一个类名称,而不是一个具体的Class对象,所以Spring第一次调用resolveBeanClass方法时,首先从BeanDefinition中得到beanClassName,将beanClassName加载成Class实例,并将该Class实例赋值给BeanDefinition的beanClass属性,而且大家需要注意,这个beanClassName有可能是一个SpEL表达式,如果是一个SpEL表达式则还需要进一步进行转换。
关键点二
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
如果我们想自己来实现创建对象的过程,可以提供一个Supplier的实现类,Spring就会在obtainFromSupplier方法中调用这个实现类的get方法来获取实例,这个是Spring为我们提供的参与创建对象的扩展点。
关键点三
如果我们指定了factoryMethod来实例化bean,什么情况下会使用factoryMethod来实例化Bean呢?一直是我们在xml配置文件中指定factoryMethod,另外一直则是通过@Bean注解来创建实例,大家想想应该就清楚了,我们在任何一个配置类中都可以通过@Bean注解来配置创建实例,那Spring肯定是要调用这个配置类的某个方法来创建,所以对于@Bean这种方式得到的DeanDefinition,还会有一个factoryBeanName来指定配置类名称。当有了factoryMethod名称,Spring会使用instantiateUsingFactoryMethod方法调用factoryMethod来创建一个实例。
关键点四
Spring通过determineConstructorsFromBeanPostProcessors这个方法来得到构造方法候选者,既然是候选者那就可能有多个,determineConstructorsFromBeanPostProcessors方法如下:
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
}
return null;
}
从代码中可以看出,这里循环调用SmartInstantiationAwareBeanPostProcessor类型后置处理器的determineCandidateConstructors方法进行处理,只要你愿意,你也可以自己实现SmartInstantiationAwareBeanPostProcessor类的determineCandidateConstructors方法来决定使用哪个构造函数,但是在正常情况下,只有AutowiredAnnotationBeanPostProcessor这个实现类的determineCandidateConstructors方法进行了处理,其他实现类都是返回null。
接下来我们说说AutowiredAnnotationBeanPostProcessor是怎么得到构造方法候选者的:
- 首先Spring调用beanClass.getDeclaredConstructors()得到当前类中所有的构造函数,然后循环构造函数,查看该构造函数上是否存在@Autowired或@Value等注解,如果没有并且当前类是cglib生成的代理类,则查看代理类的父类中对应的构造方法上是否存在@Autowired注解,如果有@Autowired注解的构造函数,则添加到candidates集合中,当然如果当前构造方法上不存在@Autowired,并且是无参构造方法,则赋值给defaultConstructor属性。
- 然后针对candidates是否有数据,进行不同处理,这里我们总结几种情况:
- 有多个@Autowired注解的构造函数,并且有一个required=true(也只能有一个,如果有多个就会报错了),也就是咱们自己指定的使用的构造函数,我们这里猜想Spring会用这个构造函数;
- 有多个@Autowired注解的构造函数,并且全部是required=false,此时如果有默认构造函数,则需要将默认构造函数添加到candidates集合中,以便后续选择;
- 没有加了@Autowired注解的构造方法,只有一个有参的构造方法,后续直接使用这个默认构造函数;
- 如果没有构造方法上添加了@Autowired注解,并且有多个构造方法则返回一个new Constructor<?>[0]数组;
- 如果只有一个无参的构造方法,这里会返回null,外层逻辑会默认使用无参构造方法进行实例化。
在得到候选构造函数后,会将构造函数添加到candidateConstructorsCache缓存中,后续如果需要使用,直接使用即可。
关键点五
在得到候选构造函数后,Spring就需要确定最终使用哪个构造函数了,针对以下几种情况需要调用autowireConstructor方法进行构造方法推断并实例化:
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
- 通过BeanPostProcessor找出了构造方法;
- BeanDefinition的autowire属性为AUTOWIRE_CONSTRUCTOR;
- BeanDefinition中指定了构造方法参数值;
- 在getBean()时指定了args参数。
否则执行instantiateBean方法使用无参的构造方法来实例化bean。
我们这里来详细分析下autowireConstructor方法,在该方法中针对构造函数大致有以下几种处理方式:
- 如果只有一个构造方法,并且没有指定构造方法参数值,则需要判断是不是无参构造方法,如果是则可以使用无参构造方法进行实例化;
- 对上述得到的构造函数集合按照参数个数降序排序,然后遍历,找到一个最合适的:
1.这里首先会根据传入的参数个数和构造函数的参数数量进行比较,如果传入的数量比构造函数数量大,那肯定不符合。
2.经过一轮比较后,觉得构造方法有用,则还需要进一步找到最合适的。当然在这之前需要得到构造函数的所有参数类型,并调用createArgumentArray方法得到这些类型对应的实例,否则怎么实例化呢?
什么是最合适的?这里Spring会根据参数类型和参数值计算权重,如果当前构造方法的权重比较小,则表示当前构造方法更合适,如果权重一样,则添加到ambiguousConstructors集合中。
关于如何计算权重的,这里简单总结下:
Spring会根据lenientConstructorResolution这个值进行计算,默认这个宽松模式值为true,在宽松模式下,会判断每个参数值的类型和当前构造方法的参数类型的距离;在非宽松模式下,会忽略每个参数值的类型和当前构造方法的参数类型的距离,只要是父子关系距离是一样的。
3.遍历完所有构造方法后,没有找到合适的构造方法,则报错;当然如果存在权重一样的构造方法并且不是宽松模式,也报错,因为权重一样,很明显Spring不知道用哪个,如果是宽松模式则不会报错,Spring会用找到的第一个;
4.最后在确定了构造函数后,使用构造函数和参数实例化对象。