上一篇从源码解读Spring的IOC讲解了spring容器的初始化以及依赖注入的过程,但是在其中有一个很重要的部分暂时还没讲。可能已经有人发现了,那篇文章虽然讲解了IOC容器创建对象的整个流程,但是好像并没有涉及我们的依赖对象以及属性是怎么注入到对象当中的,所以这篇文章就来专门分析属性注入的整个过程
首先,考虑到很多人并没有看过之前的文章,所以我并不准备接着那篇文章讲,这里采用其他方法来进行分析
这里我们有一个对象:
@Component
public class TestClass {
public TestClass() {
System.out.println("-----无参构造调用-----");
}
}
然后下面是测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestClassTest {
@Autowired
private TestClass testClass;
@Test
public void test() {}
}
执行这个测试方法,我们会发现在控制台中输出了那句话,说明我们注入变量成功了,接下来我们就在这里打上断点,来分析spring为什么会自动执行这个方法:
我们来分析这个调用栈,我们断点的位置是
我们往前看,紧接着这个方法执行前,先执行了newInstance方法,这是一个反射方法,原来spring最终是通过反射来创建我们的实例对象
然后我们再来找调用反射方法的位置,这里看到了一个BeanUtils对象,我们进入这个对象,找到instantiateClass方法:
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
除去异常捕获和非空校验,核心代码实际就两行:
// 使构造函数可显式访问
ReflectionUtils.makeAccessible(ctor);
//
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
第一行显式设置构造函数是可访问的,同时只有在必需的情况下才调用setAccessible(true)方法,以避免和JVM的安全管理发生冲突
接下来的代码中,ctor指向了构造方法,我们看这个三目运算符的条件语句:
不懂Kotlin是什么的不要紧,只需要知道这里判断注入的类是否是Kotlin类型,来选择使用Kotlin注入还是Java反射注入
我们接着看
刚才的instantiateClass方法是在SimpleInstantiationStrategy类中的instantiate方法中被调用的,我们进入这个方法:
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 如果没有覆写(重载)的方法,则不使用cglib来覆写类
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
// 获取构造方法
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
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.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
// 使用bean工具类实例化类
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 使用cglib实例化类
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
看似方法很长,实际大部分都代码段在正常情况下都不会执行到,整段代码的流程配合注释应该很容易看懂,这个方法的作用就是选择实例化bean的方式,同时为确保能够正常实例化类,对构造函数进行了预检
接着我们看其之前的方法
这个类就很熟悉了,是之前分析依赖注入过程中的核心类,整段代码这里不贴出了,只看该方法中的一段核心代码:
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
getInstantiationStrategy()从字面意思上可以看出是用来获取策略类,我们回过去看SimpleInstantiationStrategy方法,果然是一个策略类(不懂策略类的可以去了解一下设计模式中的策略模式),这里调用设置的策略类的策略方法(实例化对象的方法),最终生成被注入的对象实例,这里的三个参数分别是以下含义:
再往上就是从源码解读Spring的IOC我的这篇文章中的内容了,整个属性注入的过程就结束了
先别急,我们刚才的断点仅仅是打在构造函数处,我们也仅仅是明白了构造函数在依赖注入中的执行过程和执行时机,我们来把TestClass稍微改动一下:
@Component
public class TestClass {
@Autowired
private Demo demo;
public TestClass() {
System.out.println("-----无参构造调用-----");
}
}
这个Demo对象中仅有一个无参的构造函数,我们执行测试方法,发现先调用了Demo的构造函数,再调用了TestClass的构造函数
我们在Demo对象的构造函数里打上断点,进行调试,但是我们并不是要像刚才那样倒推执行流程,因为TestClass构造函数的执行在其之后,所以我们直接跳到下一个断点:TestClass的构造函数处,但是跳过去之后,我们发现一个很奇怪的现象,明明执行了Demo的构造函数,为什么还是不注入依赖?
别急,我们已经知道,构造方法是在AbstractAutowireCapableBeanFactory对象的doCreateBean方法中的createBeanInstance方法执行的,我们直接跳过来执行,我们重点是红框中的语句:
这里我们发现了,执行完图中红框的语句后,我们已经获取了TestClass的bean,但是其中的对象依然为空:
我们采用单步跳过的方式,发现在执行了下面这条语句后,demo对象被注入了
populateBean(beanName, mbd, instanceWrapper);
我们进入该方法,其代码如下:
/**
* @param beanName 被注入属性的bean的id
* @param mbd 被注入属性的bean
* @param bw 被注入属性的bean进行了一次封装后的包装类
*/
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 {
// 如果实例为null,就不注入
return;
}
}
boolean continueWithPropertyPopulation = true;
// 给后置处理器一次在同步前修改bean状态的机会
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// postProcessAfterInstantiation是在实例化之后,注入属性之前执行的方法,默认实现均返回true
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
// 判断是否定义了属性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// 如果autowire方式是通过名称或类型自动装配,就先进行装配
// 装配的结果并不直接添加到bean中,而是先添加到pvs中
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 判断该bean工厂中是否有应用于单例bean关闭的后置处理器
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 判断是否有依赖(继承基类)
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
// 即使没有值,也会创建一个空数组
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 在交付bean之前进行后置处理,不需要属性描述符
// 返回应用于给定bean的实际属性值
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 对给定属性值进行后置处理,同时会检查是否满足依赖,比如基于@Required注解的检查
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
// 获取bean属性的描述符
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
// 解析bean的运行时引用,使用深层复制
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
这个方法的代码看似很长,实际上核心只有一个,就是postProcessProperties方法,而我们的属性注入也正是由这个方法调用的,到这里,整个属性注入的过程才真正结束
正篇博客基本是想到哪写到哪,可能比较混乱,所以在最后重新给大家捋一捋这个单纯的属性注入的过程,这里均以AbstractAutowireCapableBeanFactory的doCreateBean()方法作为起点:
好了,这样整个IOC的流程就讲完了,整段过程错综复杂,出现错误也在所难免,欢迎各位进行指正,共同进步