Spring源码:Bean 的实例化及初始化过程

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

1. 概述

在前面的文章中,我们把 refresh() 中 obtainFreshBeanFactory、invokeBeanFactoryPostProcessors、方法都梳理了一遍,其中还有一个 registerBeanPostProcessors,但个人觉得实际上和 invokeBeanFactoryPostProcessors类似,所以没有单独写一篇文章去描述的,尽管它非常重要,但是你可以自己去分析一下或者看看这篇文章:registerBeanPostProcessors 详解

refresh() 中另外一个非常重要的方法就是 finishBeanFactoryInitialization,其中涉及到 Bean 实例化过程(即已经注册的 BeanDefinition)以及 BeanPostProcessor(实现了 BeanPostProcessor 接口的 Bean)的执行。

2. 再谈 BeanDefinition

在 Bean 的实例化之前,再来讲讲 BeanDefinition,前面说过 BeanDefinition 描述一个 Bean 的实例信息,但实际上它是一个接口,给这些信息定义了一些 getter 和 setter 方法,作为接口层它不负责定义具体的参数,AbstractBeanDefinition对这个接口进行了实现,如下:

image-20211029153956374

这个时候应该会存在一个疑问,就是为什么 Spring 实例化要通过 BeanDefinition,Claas不也是类对象吗?因为Class 是无法完成 bean 的抽象,比如 bean 的作用域,bean 的注入模型,bean 是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个 BeanDefinition 类来抽象这些信息。

2.1 AbstractBeanDefinition

AbstractBeanDefinitionBeanDefinition 的直接实现类,从类名也知道,它是一个抽象的 BeanDefinition,还不够具体。

AbstractBeanDefinition 已经对 BeanDefinition 方法有了基本实现逻辑,增加了许多新的属性(默认属性)。

2.2 RootBeanDefinition

从 Spring2.5 开始,RootBeanDefinition仅作为运行时的BeanDefinition视图。如果需要编程定义BeanDefinition,那么推荐使用GenericBeanDefinition。

Spring的解释是:GenericBeanDefinition的优势在于,它允许动态定义父依赖项,而不是一个以"硬编码"定义BeanDefinition的角色。也就是说,bean的一般形式是以GenericBeanDefinition的标准形式存在的,在特定的时机,会将GenericBeanDefinition转成RootBeanDefinition。

这段官方的解释第一次看着实让人摸不着头脑,但我们只需要记住这个 RootBeanDefinition 在实例化一个 Bean 的时候,需要与其他的 BeanDefinition 进行合并,也就是说 BeanDefinition 存在父子关系,对应的属性为parentName

而合并的目的就是不同的 BeanDefinition 存在共性,比如这个 Bean 继承了某个类或继承了某个 Bean,亦或是实现了某个接口,而合并过程就是把存在父子关系的 BeanDefinition 的属性合并起来,如果存在相同的属性,则以子Beanfinition的属性为准。

一般情况下,在 bean 的初始化时,会将 BeanDefinition 转换成 RootBeanDefinition。

至于为什么要合并,我觉得可能是为了解析成一个完整的 beandefinition 吧。

栗子

假如有这样一个动物抽象类:

@Data
public abstract class Animal {
​
    private String name;
}

然后定义一个子类 Cat:

@Data
public class Cat extends Animal{
​
    private String name;
}

然后我们的 xml 配置如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
       default-lazy-init="false">
​
    <bean id="animal" class="net.javatv.bean.Animal" abstract="true">
        <property name="name" value="animal"></property>
    </bean>
​
    <bean id="cat" class="net.javatv.bean.Cat" parent="animal"/></beans>

写个测试类:

@Test
public void test3() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Cat cat = applicationContext.getBean(Cat.class);
    System.out.println(cat.getName());
}

可以看到 cat 的 name 属性也被赋值成了animal:

image-20211029162746482

这种方式常用在一些公共的配置上,如连接池的配置,多数据源的配置等等。而这个合并的过程就在实例化bean 的过程中。

需要注意的是,RootBeanDefinition 可以单独作为一个BeanDefinition,也可以作为其他 BeanDefinition 的父类。但是他不能作为其他 BeanDefinition 的子类,这点在源码中可以很好的体现,在 setParentName 的时候,会抛出一个异常:

image-20211029161335449

2.3 ChildBeanDefinition

ChildBeanDefinition相当于一个子类,不可以单独存在,必须要依赖一个父 BeanDetintion。通过源码发现其最大的区别他的 parentName 属性是通过构造方法设置的,而且并没有提供一个无参构造方法给我们。

从 spring 2.5 开始,提供了一个更好的注册bean definition类GenericBeanDefinition,所以以后推荐使用它(这里就不做过多解释了)。

2.4 GenericBeanDefinition

GenericBeanDefinition 同样继承了 AbstractBeanDefinition,其实也就是一个普通的 BeanDefinition ,和另外 2 个不同的是,它有两个比较重要的子类:

  • AnnotatedGenericBeanDefinition,存储 @Configuration注解注释的类;
  • ScannedGenericBeanDefinition,存储@Component@Service@Controller等注解注释的类。

image-20211029170502493

2.5 BeanDefinition中的属性

  1. id:Bean 的唯一标识名。它必须是合法的 XMLID,在整个 XML 文档中唯一。

  2. name:用来为 id 创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开。

  3. class:用来定义类的全限定名(包名+类名)。只有子类 Bean 不用定义该属性。

  4. parent:子类 Bean 定义它所引用它的父类 Bean。这时前面的 class 属性失效。子类 Bean 会继承父类 Bean 的所有属性,子类 Bean 也可以覆盖父类 Bean 的属性。注意:子类 Bean 和父类 Bean 是同一个 Java 类。

  5. abstract(默认为false):用来定义 Bean 是否为抽象 Bean。它表示这个 Bean 将不会被实例化,一般用于父类 Bean,因为父类 Bean 主要是供子类 Bean 继承使用。

  6. lazy-init(默认为default):用来定义这个 Bean 是否实现懒初始化。如果为“true”,它将在 BeanFactory 启动时初始化所有的 SingletonBean。反之,如果为“false”,它只在 Bean 请求时才开始创建 SingletonBean。

  7. autowire(自动装配,默认为default):它定义了 Bean 的自动装载方式。

    • no:不使用自动装配功能。2、“byName”:通过 Bean 的属性名实现自动装配。
    • byType:通过 Bean 的类型实现自动装配。
    • constructor:类似于 byType,但它是用于构造函数的参数的自动组装。
    • autodetect:通过 Bean 类的反省机制(introspection)决定是使用 constructor 还是使用 byType。
  8. depends-on(依赖对象):这个 Bean 在初始化时依赖的对象,这个对象会在这个 Bean 初始化之前创建。

  9. init-method:用来定义 Bean 的初始化方法,它会在 Bean 组装之后调用。它必须是一个无参数的方法。

  10. destroy-method:用来定义 Bean 的销毁方法,它在 BeanFactory 关闭时调用。同样,它也必须是一个无参数的方法。它只能应用于 singletonBean。

  11. factory-method:定义创建该 Bean 对象的工厂方法。它用于下面的 factory-bean,表示这个 Bean 是通过工厂方法创建。此时,class 属性失效。

  12. factory-bean:定义创建该 Bean 对象的工厂类。如果使用了“factory-bean”则“class”属性失效。

  13. autowire-candidate:采用 xml 格式配置 bean 时,将<bean/>元素的 autowire-candidate属性设置为 false,这样容器在查找自动装配对象时,将不考虑该 bean,即它不会被考虑作为其它 bean 自动装配的候选者,但是该 bean 本身还是可以使用自动装配来注入其它 bean 的。

  14. MutablePropertyValues:用于封装<property>标签的信息,其实类里面就是有一个 list,list里面是 PropertyValue 对象,PropertyValue 就是一个 name 和 value 属性,用于封装<property>标签的名称和值信息。

  15. ConstructorArgumentValues:用于封装<constructor-arg>标签的信息,其实类里面就是有一个 map,map 中用构造函数的参数顺序作为 key,值作为 value 存储到 map 中。

  16. MethodOverrides:用于封装 lookup-method 和 replaced-method 标签的信息,同样的类里面有一个 Set 对象添加 LookupOverride 对象和 ReplaceOverride 对象。

3. finishBeanFactoryInitialization

AbstractApplicationContext#finishBeanFactoryInitialization()

该方法从名字上就可以知道是用来完成 Bean 工厂初始化的,即 Bean 的实例化,我们进入该方法,主要是看AbstractApplicationContext#preInstantiateSingletons()

image-20211101095025374

进入 preInstantiateSingletons() 方法:

public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }
    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    //xml解析时,讲过,把所有beanName都缓存到beanDefinitionNames了
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        //把父BeanDefinition里面的属性拿到子BeanDefinition中,也就是上面讲的BeanDefinition合并
        //该方法可自行具体研究,这里暂不讨论
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        //如果不是抽象的,单例的,非懒加载的就实例化
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            //判断bean是否实现了 FactoryBean 接口,这里可以先不看
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                                (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                                getAccessControlContext());
                    } else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            } else {
                //主要看实例化过程,如果不是FactoryBean,则及时普通的Bean
                getBean(beanName);
            }
        }
    }
    // Trigger post-initialization callback for all applicable beans...
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    smartSingleton.afterSingletonsInstantiated();
                    return null;
                }, getAccessControlContext());
            } else {
                smartSingleton.afterSingletonsInstantiated();
            }
        }
    }
}

3.1 getBean

按照上面的分析过程,进入AbstractBeanFactory#getBean()

public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

可以看到调用了doGetBean方法,在 Spring 中一般涉及到 doXxx就是真正干实事的方法,而该方法也不是说从头看到底,我们找到最关键的代码出,如下:

image-20211101112053973

为什么先看这里,在我们学习 Spring 的时候,尽管没看过源码我们也知道 Spring 创建实例默认是单例的,也就是说大多数情况下都是单例 Bean。

3.2 getSingleton

DefaultSingletonBeanRegistry#getSingleton()

初次实例化 bean 通过 getSingleton 方法:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        // 从 一级缓存 中拿取实例,如果存在直接返回,或者创建 bean
        // 常见的就是容器创建的时候创建实例,然后如果通过@Autowired注入,则会在次调用getBean,从缓存中拿取实例
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            /**
             * 把 beanName 添加到 singletonsCurrentlyInCreation Set 容器中,在这个集合里面的 bean 都是正在实例化的
             * 实际就是实例化还没完成的 BeanName,会在循环依赖的地方判断是否正在实例化
             */
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 调到 getObject 方法,完成 bean 的实例化,即调用createBean()方法
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            } catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            } finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                /**
                 * 到这里,Bean已经完成实例化,然后要做 2 件事
                 * 1、singletonsCurrentlyInCreation 把 beanName 从这个集合中删除
                 * 2、addSingleton,把 bean 缓存到一级缓存中
                 */
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

这里存在 2 种情况,第一是可以直接从缓存中拿取实例,第二种就是需要创建实例,调用getObject(),我们先看第二种情况,调用链路往下为AbstractAutowireCapableBeanFactory#createBean()

3.3 createBean

在该方法中,主要看doCreateBean()

image-20211101150427551

可以看到,这里出现了一个新的接口BeanWrapper,其实就是一个 Bean 的包装器,包括对 Bean 的属性、方法,数据等,同时它还具备属性转换的能力,因为它还得是一个类型转换器,它有唯一的一个实现类是BeanWrapperImpl,具体的可以看看这篇文章:Spring中的BeanWrapper及类型转换

那么他和 BeanDefinition有什么关系呢?

BeanDefinition(原料)->BeanFactory(工厂)->BeanWrapper(产品)

然后,进入 Bean 实例化核心方法AbstractAutowireCapableBeanFactory#createBeanInstance(),并且包装成 BeanWrapper 对象。

4. 实例化的几种情况

在进入createBeanInstance()方法之前,先来讲讲在实例化过程中的几种情况:

  1. factoryMethodName,即 BeanDefinition 中的 factoryMethodName 属性;
  2. 有 @Autowired 有参构造函数;
  3. 无 @Autowired 有参构造函数;
  4. 无参构造函数。

4.1 factoryMethodName

在 BeanDefinition 有 factory-method 属性的会实例化。在源码中体现如下:

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

举个栗子

新建 2 个类,并且不添加 @Conponent 注解:

FactoryMethodBean

public class FactoryMethodBean {

    public Object method() {
        return new Handsome();
    }
}

Handsome

import lombok.Data;

@Data
public class Handsome {

    private String name = "ayue";
}

xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
       default-lazy-init="false">

    <bean id="factoryMethodBean" class="net.javatv.bean.FactoryMethodBean"/>
    <!-- 没有class属性 -->
    <bean id="handsome" factory-bean="factoryMethodBean" factory-method="method"/>

</beans>

测试类

@Test
public void test4() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Handsome man = applicationContext.getBean(Handsome.class);
    System.out.println(man.getName());
}

根据断点定位可以看到:

image-20211101171002709

也就是说 Handsome 类也被实例化了,底层源码就是通过反射来创建实例,这里就不贴源码了,可自行分析。

其中需要注意的是,factoryMethodName 除了上面的方式,还有一种就是类的静态方法也是可以实例化的,如下:

import lombok.Data;

@Data
public class Handsome {

    private String name = "ayue";

    public static Object method() {
        return new Handsome();
    }
}

另外,Spring 会扫描有@Bean注解的方法,然后把方法名称设置到 BeanDefinition 的 factoryMethod 属性中,然后就会调到上面的方法实现 @Bean方法的调用,@Bean的具体流程后续在分析。

4.2 @Autowired 有参构造函数

如果不是 factoryMethodName ,则进入下一个方法determineConstructorsFromBeanPostProcessors,这 个 方法是 BeanPostProcessor 接口类的应用,最终会调到 AutowiredAnnotationBeanPostProcessor 类的方法, 在方法中会扫描有注解的构造函数然后完成装配过程,然后把有有@Autowired 注解的构造函数返回。

举个栗子

实例 A

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class A {

    private String name = "a";
}

实例 B

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class B {

    private String name = "b";
}

@Autowired 有参构造函数

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class AutowiredConstructBean {
	
    //只有一个构造函数的情况
    @Autowired
    public AutowiredConstructBean(A a, B b) {
        System.out.println(a.getName());
        System.out.println(b.getName());
    }
}

测试结果:

image-20211102090615797

可以看到 A 和 B 都被实例化。

此时有另外一个问题,如果有两个构造函数呢?如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class AutowiredConstructBean {

    @Autowired
    public AutowiredConstructBean(A a, B b) {
        System.out.println(a.getName());
        System.out.println(b.getName());
    }
    
    @Autowired
    public AutowiredConstructBean(A a) {
        System.out.println(a.getName());
    }
}

结果如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'autowiredConstructBean': Invalid autowire-marked constructor: public net.javatv.autowired.AutowiredConstructBean(net.javatv.autowired.A). Found constructor with 'required' Autowired annotation already: public net.javatv.autowired.AutowiredConstructBean(net.javatv.autowired.A,net.javatv.autowired.B)

image-20211102093052981

意思是已经存在了构造器,所以直接抛出异常了。

我们可进入源码分析AbstractAutowireCapableBeanFactory#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;
}

然后进入AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors()方法:

1、其中第一个 if 代码块我们可以忽略,即 @Lookup 方法注入,因为不怎么使用了,如果你想了解,可以看这篇文章:Spring中的Lookup

image.png

2、获取有参构造函数

image-20211102102700459

从代码 debug 也能看到:

image-20211102102959585

3、判断构造函数是否有 @Autowired 注解

image-20211102103657626

此时,涉及到多个构造函数的情况,如果只有一个且@Autowired 的 required 属性为 true,则正常执行。如果存在多个构造函数且required=true,就会进入requiredConstructor != null为 true,抛出异常,即上面代码演示的情况。

因此,如果要解决多个构造函数不抛异常则需要设置required=false,此时该段代码并不会执行:

image-20211102105703048

代码演示如下:

@Component
public class AutowiredConstructBean {

    @Autowired(required = false)
    public AutowiredConstructBean(A a, B b) {
        System.out.println(a.getName());
        System.out.println(b.getName());
    }

    @Autowired(required = false)
    public AutowiredConstructBean(A a) {
        System.out.println("第 2 个构造函数" + a.getName());
    }
}

再去看看结果:

image-20211102105405066

从结果可以看出,有 2 个参数的构造函数打印出了结果,而 1 个参数的并没有打印出来。这是为什么呢?

这是因为 Spring 做了一个排序操作并且取了参数最多的构造函数来实例化,具体方法在ConstructorResolver#autowireConstructor中的这段代码:

AutowireUtils.sortConstructors(candidates);

同时,在拿到构造函数以后,通过 autowireConstructor()进行参数的解析和实例化,这个过程相对来说比较复杂,我们先知道大致流程即可,但最后实例化的时候仍然调用的是 getBean() 方法来实例化的。

实际上,不管是 Field、Method、还是构造函数中有@Autowired 注解引入的类,都是通过 getBean() 方法进行实例化的。

4.3 无 @Autowired 有参构造函数

对于没有 @Autowired 注解的有参构造函数,当只有一个有参构造函数的时候正常执行,但有 2 个的时候同样会报错,但不同于上述错误。

代码演示:

@Component
public class AutowiredConstructBean {

	// 去掉@Autowired注解
    public AutowiredConstructBean(A a, B b) {
        System.out.println(a.getName());
        System.out.println(b.getName());
    }

    public AutowiredConstructBean(A a) {
        System.out.println("第 2 个构造函数" + a.getName());
    }
}

错误显示找不到默认的构造函数:

image-20211102114206559

为什么会出现这种情况,通过源码分析,经过一系列的判断之后走向了如下方法:

image-20211102114548232

即最后返回了空的构造器,所以抛出异常,那么怎么解决呢?

其实只要添加一个无参构造器就行了。

@Component
public class AutowiredConstructBean {


    public AutowiredConstructBean() {
        System.out.println("添加无参构造函数");
    }

    public AutowiredConstructBean(A a, B b) {
        System.out.println(a.getName());
        System.out.println(b.getName());
    }

    public AutowiredConstructBean(A a) {
        System.out.println("第 2 个构造函数" + a.getName());
    }
}

这也说明了一个问题,就是在 Spring 中,如果你的实体类是有参的构造函数,那么必须添加一个无参的构造函数。如果没有那就是默认的无参构造函数,也就是最常见的第 4 种情况。

4.4 无参构造函数

无参构造函数实例化,就是AbstractAutowireCapableBeanFactory#instantiateBean(),其实现就是简单的反射实现,并且大部分情况都是通过该种方式实现。

5. IOC/DI

在实例化完成之后,需要对类中的属性进行依赖注入操作,对于什么是IOC,什么是DI,似乎不应该在这里讲,不管是面试还是学习 IOC 和 DI 都是耳熟能详的,但可能还是有人对两个概念是模糊不清,所以这里总结一下。

5.1 IOC控制反转

首先说说 IOC,中文翻译为控制反转,它并不是一种技术,而是一种思想,那么什么叫反转?有反转那就有正转。

举个栗子

好比我们吃饭,常规情况下我们需要先去买菜,然后还需要准备调料,厨具等,然后还需要自己来炒菜,这一整个过程相当复杂,并且一旦其中缺了一个环节都不能达到目标,类比到对象中就是如果对象 A 需要对象 B,那么在A中我们就需要通过 new A()的方式来获取实例 B,然后使用完之后还需要将对象销毁,比如数据库连接等,耦合度非常高,这种方式可以理解为正转。

而 IOC 的做法则不同,照样是吃饭,但是我们可以在外卖APP上点外卖,APP上有很多可以供我们选择,而我要做的就是我需要吃什么,外卖员送过来即可,如果送错了,那就抛出异常即可,整个做饭的过程都不需要我们来控制,这里的外卖APP就类似一个容器。

Spring 所倡导的开发方式就是如此,所有的类都会在 Spring 容器中登记,告诉 Spring 你是个什么东西,你需要什么东西,然后 Spring 会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 Spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制,所以这叫控制反转。

5.2 DI 依赖注入

DI,Dependency Injection,即依赖注入,我把他理解为是对 IOC 的实现,按照依赖和注入我们把它拆分一下:

  1. 谁依赖谁

    应用程序依赖于 IOC 容器,这里的应用程序可以是一个类,也可以是一整个项目;

  2. 为什么需要依赖

    应用程序需要依靠 IOC 容器来提供外部所需要的资源(对象等);

  3. 谁注入谁

    IOC 容器注入了某个需要被依赖的资源(对象等);

  4. 注入了什么

    容器外部所需要的资源(对象等)。

举个栗子

对象 A 需要操作数据库,以前我们总是要在 A 中自己编写代码来获得一个 Connection 连接对象,有了 Spring 我们就只需要告诉 Spring,A 中需要一个 Connection,至于这个 Connection 怎么构造,何时构造,A 不需要知道。在系统运行时,Spring 会在适当的时候制造一个 Connection,然后像打针一样,注射到 A 当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个 Connection是由 Spring注入到A中的,依赖注入的名字就这么来的。那么 DI 是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,Spring 就是通过反射来实现注入的。

而在 Spring 中常见的对类的属性或方法的注入方式就是 @Autowired或者 @Resource注解,因此,在反射之前,需要先进行对类中的注解的收集。

6. 注解的收集

关于对 @Autowired或者 @Resource注解的收集,我们回到 3.3 的doCreateBean()方法中,在实例化之后,通过调用applyMergedBeanDefinitionPostProcessors()方法来完成注入:

image-20211102152746779

进入该方法可以发现,实际上是通过对 BeanPostProcessor 接口来调用的如下:

image-20211102155654166

其中可以看到有 2 个重要的实现类CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor

6.1 @PostConstruct & @PreDestroy

CommonAnnotationBeanPostProcessor 这个类完成了对@Resource 注解的属性或者方法的收集,此外,这个类还对@PostConstruct@PreDestory 支持。

image-20211102161303309

其构造方法如下:

image-20211102161406397

这也就是为什么我们在项目中可能会看到被@PostConstruct注解标记的方法,因为会在实例化的时候加载注入。

进入postProcessMergedBeanDefinition(),具体的收集过程如下:

1、查看缓存里面有没有 InjectionMetadata 对象;

image-20211102163305622

2、循环遍历类中的所有的方法,判断方法上是否有@PostConstruct注解和@PreDestroy 注解,然后通过反射调用,如下图所示:

image-20211102164447221

6.2 @Resource

1、看缓存里面有没有 InjectionMetadata 对象 。

image-20211102162022902

2、从类中获取所有 Field 对象,循环 field 对象,判断 field 有没有@Resource 注解,如果有注解封装成 ResourceElement 对象。

image-20211102165557630

3、最终把两个 field 和 Method 封装的对象集合封装到 InjectionMetadata 对象中。

image-20211102165904897

6.3 @Autowired & @Value

AutowiredAnnotationBeanPostProcessor类是对 @Autowired 和 @Value 注解的属性和方法的收集,构造方法如下:

image-20211102170251618

其方法 findAutowiringMetadata() 是对注解进行解析:

image-20211103092232516

收集过程同@Resource,也是收集 Field 和 Method 上的注解,然后放到 InjectionMetadata 对象中。

image-20211102171354458

7. 依赖注入

Bean 实例化完成、并且收集完@Resource 和@Autowired 注解以后就开始依赖注入,还是在doCreateBean()方法中:

image-20211102171925640

进入AbstractAutowireCapableBeanFactory#populateBean()方法。

7.1 populateBean

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 {
         // Skip property population phase for null instance.
         return;
      }
   }
   // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
   // state of the bean before properties are set. This can be used, for example,
   // to support styles of field injection.
   /**
    * 属性填充判断:这里调用了 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation方法
    * 给InstantiationAwareBeanPostProcessor最后一次机会在属性设置前来改变bean
    * 注意:如果实现这个类并且在postProcessAfterInstantiation()返回 false 可以导致其他实例无法注入
    */
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
               return;
            }
         }
      }
   }
   PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
   int resolvedAutowireMode = mbd.getResolvedAutowireMode();
   // 自动装配:根据名称或类型自动注入,从Spring2.5开始,开始支持使用注解(@Autowired)来自动装配Bean的属性
   if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
      // Add property values based on autowire by name if applicable.
      if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
         // 名称注入
         autowireByName(beanName, mbd, bw, newPvs);
      }
      // Add property values based on autowire by type if applicable.
      if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
         // 类型注入
         autowireByType(beanName, mbd, bw, newPvs);
      }
      pvs = newPvs;
   }
   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;
            // 依赖注入过程
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
               if (filteredPds == null) {
                  filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
               }
               // 老版本用这个方式去注入
               pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvsToUse == null) {
                  return;
               }
            }
            pvs = pvsToUse;
         }
      }
   }
   if (needsDepCheck) {
      if (filteredPds == null) {
         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
      // 依赖检查,对应 depends-on 属性,3.0 已弃用,这里不在分析
      checkDependencies(beanName, mbd, filteredPds, pvs);
   }
   if (pvs != null) {
      // 将属性应用到bean中,这种一般是 XMl properties的方式,现在基本没有使用,所以不在分析
      applyPropertyValues(beanName, mbd, bw, pvs);
   }
}

@Autowired为例,当注入了某个类时,如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class AutowiredConstructBean {

    @Autowired
    private A a;

    public String init(){
        return a.getName();
    }
}

测试类:

@Test
public void test4() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    AutowiredConstructBean bean = applicationContext.getBean(AutowiredConstructBean.class);
    System.out.println(bean.init());
}

输出:

image-20211103094710436

进入 populateBean()源码,先看这段代码:

image-20211103100114983

它会遍历 InstantiationAwareBeanPostProcessor 类型的接口进行注入,而在上面注解的收集讲到了它的实现类之一就是AutowiredAnnotationBeanPostProcessor,所以当存在@Autowired注入时,则进入AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法。

7.2 postProcessProperties

进入该方法,首先会收集 Bean 中是否存在@Autowired注解,并放到 InjectedElement 中:

image-20211103100741639

如上面例子中的 AutowiredConstructBean 中注入了 A,通过debug可以看到:

image-20211103101336963

7.3 inject

收集到注入对象之后开始注入,进入inject() 方法:

image-20211103102634951

debug 上面的例子可以很明显的看到:

image-20211103102555578

然后在进入element.inject循环注入,我们再看看该方法:

image-20211103103108177

需要注意的是,上面方法中的 target 是为 null 的,是因为在该方法中可以看到注入的方式有两种,字段和方法,所以它有 2 个具体的实现方法,如下:

image-20211103105012461

所以我们这里看字段注入,即内部类 AutowiredFieldElement 中的 inject 方法:

image-20211103110940661

该方法简单来说就是拿到需要注入的实例对象,完成注入即可,而对象的获取就是通过 getBean 方法来获取的,在单例模式下,一般来说容器初始化的时候会创建实例,然后通过 @Autowried 注入时,会直接从缓存中获取。

8. initializeBean

在执行完Bean的创建和IOC的依赖注入之后,然后就进入doCreateBean()中的 initializeBean(),初始化 Bean。

image-20211103191629017

8.1 Aware 接口

首先是对 Aware 接口的调用,Aware 是一个空扩展接口,具体的接口定义由子类实现,通常用于属性设置,如 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。功能的实现主要在invokeAwareMethods方法中。

image-20211103145225136

当Bean实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware三个接口时,就会在Bean初始化的时候去调用对应的set方法,设置对应的属性。具体的扩展暂时没有,所以可以先了解。

另外还有一个最常见的实现类 ApplicationContextAware,也是我们在项目中经常使用到的,也就是Spring上下文,我们可以用它来获取我们需要的 Bean,如下:

@Component
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext appContext = null;

    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return appContext.getBean(name);

    }

    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return appContext.getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return appContext.getBean(name, clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (appContext == null) {
            appContext = applicationContext;
        }
    }
}

而对于 ApplicationContextAware 中的 ApplicationContext 的赋值就在 applyBeanPostProcessorsBeforeInitialization 中。

8.2 applyBeanPostProcessorsBeforeInitialization

在 Bean 初始化前,调用所有 BeanPostProcessors 的 postProcessBeforeInitialization 方法,在 refresh() 中会进行BeanPostProcessors 的注册,即registerBeanPostProcessors,而 BeanPostProcessors 中有 2 个重要的方法。

image-20211103192702440

1、ApplicationContextAwareProcessor,它是 BeanPostProcessors 的一个重要实现类,它在postProcessBeforeInitialization 中对 ApplicationContext 进行了赋值,代码如下:

image-20211103193455218

进入 invokeAwareInterfaces:

image-20211103193557356

很明显可以看到和 initializeBean 中的 invokeAwareMethods 一致。

2、InitDestroyAnnotationBeanPostProcessor,这个实现类主要就是对 @PostConstruct 注解方法的调用,上面讲的是收集,而这里就是具体的调用。

image-20211103200543360

3、ImportAwareBeanPostProcessor,该方法是对 ImportAware 实例进行调用,在 Spring Boot 中,我们经常看到@import就和它有关,这里就不做过多描述,感兴趣可自行研究。

image-20211103201605337

8.3 invokeInitMethods

image-20211103204108017

该方法初始化 Bean 主要有两种情况:

1、Bean 实现了 InitializingBean 接口,则这个 Bean 在初始化的时候会调用 afterPropertiesSet() 方法,你也可以自己实现它,然后做自己想做的事,比如配置文件的解析,缓存预热,缓存数据加载到内存等等。

2、init-method 标签直接注入bean,如下面的例子:

public class InitMethodBean {

    public void init(){
        System.out.println("init-method初始化...");
    }
}

xml 配置

<bean id="initMethodBean" class="net.javatv.autowired.InitMethodBean" init-method="init"/>

测试结果

image-20211103204947652

需要注意的是,Spring 虽然可以通过 InitializingBean 完成 Bean 初始化后对这个 Bean 的回调,但是这种方式要求Bean实现InitializingBean接口。一但Bean实现了InitializingBean接口,那么这个Bean的代码就和Spring耦合到一起了。而 init-method并不依赖于Spring的某个接口,但它是经过反射来执行的,效率上低于InitializingBean

对于init-method,Spring 要求它是一个无参的方法,如果init-method指定的方法中有参数,那么 Spring 将会抛出异常,如下:

image-20211103212914638

另外,init-method指定的方法可以是声明的抛出异常,如下:

public class InitMethodBean {

    public void init() throws Exception {
        
        System.out.println("init-method初始化...");
        
        if (true) {
            throw new Exception("init exception...");
        }
    }
}

afterPropertiesSetinit-method 和有@PostConstruct 注解的方法其实核心功能都是一样的,都是在该类实例化和 IOC 做完后调用的,只是调用时序不一样,从上面的代码分析可以知道他们的调用时序为:

@PostConstruct > afterPropertiesSet > init-method

9. 总结

本文主要讲的是 Bean 的实例化,到 Bean 的依赖注入,再到 Bean 的初始化三个过程,还有 BeanPostProcessor 以及 Aware 接口的扩展,当然,其中还有很多没分析到,如 getBean() 方法中的细节,FactoryBean的创建,循环依赖等等,但大致流程可以明确,后续在对具体的细节进行补充。

10. 参考

猜你喜欢

转载自juejin.im/post/7113412743759134750