优秀的编程知识分享平台

网站首页 > 技术文章 正文

Spring框架系列之bean的生命周期底层原理之如何推断构造函数?09

nanyue 2024-09-07 16:43:29 技术文章 8 ℃

上一篇笔者漏掉了一块内容,关于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是否有数据,进行不同处理,这里我们总结几种情况:
  1. 有多个@Autowired注解的构造函数,并且有一个required=true(也只能有一个,如果有多个就会报错了),也就是咱们自己指定的使用的构造函数,我们这里猜想Spring会用这个构造函数;
  2. 有多个@Autowired注解的构造函数,并且全部是required=false,此时如果有默认构造函数,则需要将默认构造函数添加到candidates集合中,以便后续选择;
  3. 没有加了@Autowired注解的构造方法,只有一个有参的构造方法,后续直接使用这个默认构造函数;
  4. 如果没有构造方法上添加了@Autowired注解,并且有多个构造方法则返回一个new Constructor<?>[0]数组;
  5. 如果只有一个无参的构造方法,这里会返回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.最后在确定了构造函数后,使用构造函数和参数实例化对象。

最近发表
标签列表