Spring源码之容器如何调用类的构造方法进行bean实例化?

文章目录

  • 前言
  • 实例化的两种方式
    • 1. 显式实例化
    • 2. 隐式实例化
  • 总结


前言

  Spring对bean的实例化,实际上也是调用类的构造方法进行实例化对象的,这篇文章单独针对Spring如何调用类的构造方法来创建对象进行源码分析。


实例化的两种方式

  bean实例化的过程我们主要看createBeanInstance这个方法,这里面有个分支逻辑,如果bean配置了factoryMethod方法,factoryMethod里面一定是对bean的实例化,通过factoryMethod方法可以暴露给用户实例化过程的叫做显示实例化;如果没有配置工厂方法,bean实例化的细节不暴露给用户,则是所谓的隐式实例化。

1. 显式实例化

if (mbd.getFactoryMethodName() != null) {
     
	return instantiateUsingFactoryMethod(beanName, mbd, args);
}

如果bean配置了factoryMethod,则看instantiateUsingFactoryMethod这个方法:

// 获取bean的factoryBeanName
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
     
	// bean的factoryBeanName不能是和bean相同,否则会报错
	if (factoryBeanName.equals(beanName)) {
     
		throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
				"factory-bean reference points back to the same bean definition");
	}
	// 获取factoryBean对象
	factoryBean = this.beanFactory.getBean(factoryBeanName);
	if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
     
		throw new ImplicitlyAppearedSingletonException();
	}
	factoryClass = factoryBean.getClass();
	// factoryMetho为非静态方法
	isStatic = false;
}
else {
     
	// 如果bean没有配置factoryBean,那么必须配置class
	if (!mbd.hasBeanClass()) {
     
		throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
				"bean definition declares neither a bean class nor a factory-bean reference");
	}
	factoryBean = null;
	factoryClass = mbd.getBeanClass();
	// factoryMethod需要为静态方法
	isStatic = true;
}

在配置文件,bean可以配置以下属性:

<bean id="" class="" factory-bean="" factory-method="">bean>

从上面的分支条件可以总结出有两种情况:

  1. bean配置了factoryBean,那么factoryBean不能和bean相同,factoryBean就是bean的工厂bean,工厂方法是非静态的。
  2. bean没有配置factoryBean,那么必须配置class,该bean作为自己的工厂bean,工厂方法是静态的。
if (candidates == null) {
     
	candidates = new ArrayList<>();
	// 得到工厂类的所有方法,包括父类的方法
	Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
	for (Method candidate : rawCandidates) {
     
		// 判断是否符合上面的分支条件,并且过滤到工厂方法放到candidates中
		if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
     
			candidates.add(candidate);
		}
	}
}

将工厂方法统一放到candidates中,这里就会产生分支条件:

  1. 如果只有一个工厂方法且没有参数,那么直接instantiate即可。
  2. 否则排序后,拿到适合的工厂方法和参数,进行instantiate。
return this.beanFactory.getInstantiationStrategy().instantiate(
			mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);

这里采取了策略模式,instantiate方法其实很简单,本质上还是采用反射进行实例化。

try {
     
	currentlyInvokedFactoryMethod.set(factoryMethod);
	Object result = factoryMethod.invoke(factoryBean, args);
	if (result == null) {
     
		result = new NullBean();
	}
	return result;
}

2. 隐式实例化

如果bean没有配置工厂方法,那么就要获取bean的有效构造方法

Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

那我们来看一下具体的细节

if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
     
	for (BeanPostProcessor bp : getBeanPostProcessors()) {
     
		if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
     
			SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
			// 这里只有AutowiredAnnotationBeanPostProcessor对此进行了处理
			Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
			if (ctors != null) {
     
				return ctors;
			}
		}
	}
}

让我们进入determineCandidateConstructors中:

// 首先获取bean的所有构造方法
rawCandidates = beanClass.getDeclaredConstructors();
// 循环构造方法
for (Constructor<?> candidate : rawCandidates) {
     
	// 返回方法上的@Autowire和@Value注解
	MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
	if (ann != null) {
     
		// 注解上面的required属性
		boolean required = determineRequiredStatus(ann);
		if (required) {
     
			// 如果required为true,则只能有一个注解修饰的构造方法
			requiredConstructor = candidate;
		}
		// 将注解修饰的构造方法放入到candidates中
		candidates.add(candidate);
	} else if (candidate.getParameterCount() == 0) {
     
		// 判断是否无参构造方法
		defaultConstructor = candidate;
	}
}
if (!candidates.isEmpty()) {
     
	if (requiredConstructor == null) {
     
		if (defaultConstructor != null) {
     
			// 如果没有required为true的注解修饰的构造方法,
			//则把默认构造方法加入到candidates中
			candidates.add(defaultConstructor);
		}
	}
	// 集合转换为数组Constructor[]
	candidateConstructors = candidates.toArray(new Constructor<?>[0]);
} else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
     
	//如果只有一个不加注解的有参构造函数
	candidateConstructors = new Constructor<?>[] {
     rawCandidates[0]};
} else {
     
    // 否则为空数组
	candidateConstructors = new Constructor<?>[0];
}

返回构造方法的数组后,这里就有了两种情况:

  1. 数组为空
	return instantiateBean(beanName, mbd);
  1. 数组不为空
	return autowireConstructor(beanName, mbd, ctors, args);

这里主要看数组不为空的情况:

return new ConstructorResolver(this)
	.autowireConstructor(beanName, mbd, ctors, explicitArgs);
// 将构造方法进行排序
AutowireUtils.sortConstructors(candidates);
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);
		}
	}
}

遍历出最匹配的构造函数,并把该构造函数的参数实例化之后调用:

bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));

instantiate中又是怎么操作的呢?

// 构造函数的参数
Class<?>[] parameterTypes = ctor.getParameterTypes();
for (int i = 0 ; i < args.length; i++) {
     
	if (args[i] == null) {
     
		Class<?> parameterType = parameterTypes[i];
		argsWithDefaultValues[i] = (parameterType.isPrimitive() 
		? DEFAULT_TYPE_VALUES.get(parameterType) : null);
	}
	else {
     
		argsWithDefaultValues[i] = args[i];
	}
}

得到参数的值放到argsWithDefaultValues数组中,之后

return ctor.newInstance(argsWithDefaultValues);

本质上还是利用了构造函数反射出bean实例。


总结

经过源码分析总结出:

  1. 可以通过工厂方法实例化bean,包括静态工厂方法和非静态工厂方法
  2. 静态工厂方法,必须配置class属性,且bean和工厂bean是同一个
  3. 非静态工厂方法,不仅仅需要配置factory-method,还需要配置factory-bean
  4. 如果bean只有一个构造函数时,IOC容器调用该构造函数实例化bean
  5. 如果bean有多个构造函数,且没有@Autowire和@Value注解修饰时,默认调用无参构造函数实例化bean
  6. 如果有注解修饰的多个构造方法,则进行排序,选取构造函数中参数最多的那个进行实例化bean
  7. 需要注意的是,如果有注解修饰多个构造方法,那么注解的required属性的值必须为false,默认是true,否则报错。

你可能感兴趣的:(Spring源码,spring,源码,java)