Springboot启动流程核心知识点(二):bean的实例化过程

5 篇文章 1 订阅

目录

1 获取beanName

2 三级缓存获取单实例Bean

3 解析缓存实例

3.1 缓存获取单实例bean

4 创建实例

4.1 解析@DependsOn注解

4.1.1 @DependsOn循环依赖问题

4.2 以单例的方式创建Bean

4.2.1 创建Bean:createBean

4.3 以多态的方式创建Bean

4.4 以其他的方式创建Bean

4.5 判断生成bean实例是否与requiredType兼容

5 总结


在Springboot的启动过程中,当执行到PostProcessorRegistrationDelegate类中的

invokeBeanFactoryPostProcessors

方法时,会通过执行

beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)

来通过beanName和接口,来获取其下一系列实现类的实例。

依旧通过源码来了解你完整的原理,首先进入AbstractBeanFactory类:

public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return this.doGetBean(name, requiredType, (Object[])null, false);
    }

然后进入真正的doGetbean方法,下面我们通过分段讲解来了解其原理:

1 获取beanName

String beanName = this.transformedBeanName(name);

顾名思义,这一步就是通过传入的name参数来获取在容器中定义的beanName,具体执行分两步。

第一步:

如果传入参数name以&字符开头,则以此字符为分割点,取后面所有字符串返回,若返回的字符串依旧含有&,则继续以此字符串为分割点,取后面字符,直到返回的字符串不再包含&为止。在取到合适的beanName后,把name、beanName以key-value的形式存到缓存transformedBeanNameCache中,同时返回beanName。

如果传入参数name不以&字符开头,则直接返回。

public static String transformedBeanName(String name) {
        Assert.notNull(name, "'name' must not be null");
        return !name.startsWith("&") ? name : (String)transformedBeanNameCache.computeIfAbsent(name, (beanName) -> {
            do {
                beanName = beanName.substring("&".length());
            } while(beanName.startsWith("&"));
            return beanName;
        });
    }

说起来挺绕,直接看源码就清晰许多,这个循环解析字符串的思想也可以学习一下。

第二步:

取到第一步返回的beanName,在别名缓存aliasMap中查看当前beanName的值是否存在别名,如果不存在,直接返回beanName,若存在,返回别名,注意这里的别名有可能存在嵌套别名,所以还需要在aliasMap循环解析。

public String canonicalName(String name) {
        String canonicalName = name;
        String resolvedName;
        do {
            resolvedName = (String)this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        } while(resolvedName != null);
        return canonicalName;
    }

2 三级缓存获取单实例Bean

Object sharedInstance = this.getSingleton(beanName);

通过方法名便可以看出,其是通过beanName去IOC容器获取对应的单例实例,前提是如果存在。

这个方法其实就是Spring中常考的一个面试题,Spring是如何解决循环依赖的,通常我们会回答是通过三级缓存去查找Bean实例,先从一级缓存找,找不到再去二级缓存,如果二级缓存也找不到就去三级缓存找,在三级缓存找到后,把实例放入二级缓存,然后删除三级缓存对应实例。如果不了解源码,真的像背八股文一样,但是如果看了源码,一切就很明了了:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //一级缓存通过beanName获取实例,其实就是一个Map
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果一级缓存未找到,且是当前正在创建的单实例
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            //以一级缓存对象为锁,设置同步块
            synchronized(this.singletonObjects) {
                //从二级缓存通过beanName获取实例
                singletonObject = this.earlySingletonObjects.get(beanName);
                //如果二级缓存为空,且参数allowEarlyReference为true
                if (singletonObject == null && allowEarlyReference) {
                    //从三级缓存获取beanName对应beanFactory
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //获取实例
                        singletonObject = singletonFactory.getObject();
                        //singletonObject取到值后,放入二级缓存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        //删除三级缓存
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        //返回实例
        return singletonObject;
    }

通过源码也可以清晰的知道到,既然是缓存,如果三级缓存都取不到,也只能返回空了。

关于这个从缓存获取单例bean的方法this.getSingleton(beanName),再多说几句。

由于doGetBean是Spring框架专门用来生成bean实例的通用方法,所以无论bean实例是单例还是多态,抑或是其他形式,都可以走这个方法来依次从三级缓存中获取bean实例,只不过在后面实际生成bean实例的过程中,只有单例模式的bean实例才会被放入三级缓存,所以其实这个方法对多态或其他模式的bean实例,是永远返回null的。

这样,后面的逻辑在生成单实例bean的时候,还可以继续处理多态和其他形式的bean实例生成。这样通过一个方法,满足多方面的需求,才是Spring作为一个框架的良苦用心。

3 解析缓存实例

if (sharedInstance != null && args == null) {
            ......
            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);

如果从缓存中返回的实例不为空,则对其进一步处理,具体又分为三种情况来处理。

第一种,如果传入的name参数不为空,且以&字符开头,表明它是FactoryBean的间接引用,如果此时bean的定义信息不为空,则设置其RootBeanDefinition的isFactoryBean属性为true后,直接返回sharedInstance。

第二种,如果传入的sharedInstance参数不是FactoryBean类型,直接返回。

第三种,不满足以上两种情况的其余情况,这种情况就稍微复杂一些,需要根据sharedInstance和RootBeanDefinition参数的状态来判断。那么依然是进行逐步分段讲解,会更清晰。

首先会定义一个为null的object对象,如果此时传入的RootBeanDefinition参数不为空,则设置其isFactoryBean属性为true,否则从缓存中根据beanName获取对应FactoryBean实例:

Object object = null;
if (mbd != null) {
   mbd.isFactoryBean = true;
} else {
   object = this.getCachedObjectForFactoryBean(beanName);
}

如果经历以上步骤,object不为空,直接返回object,否则,先把传入参数beanInstance强制转换为FactoryBean类型。

此时如果传入的RootBeanDefinition参数为空,但是如果beanFactory的bean信息缓存beanDefinitionMap中,存在当前beanName的缓存信息,则从合并bean信息缓存mergedBeanDefinitions中通过beanName获取bean定义信息,如果缓存mergedBeanDefinitions取得的bean定义信息不满足要求,则通过beanName获取对应缓存mergedBeanDefinitions中的信息,合并到beanDefinitionMap取出的bean定义信息中,返回最终合并的bean定义信息,其主要逻辑在getMergedLocalBeanDefinition方法中:

if (object == null) {
   FactoryBean<?> factory = (FactoryBean)beanInstance;
   if (mbd == null && this.containsBeanDefinition(beanName)) {
      mbd = this.getMergedLocalBeanDefinition(beanName);
   }

之后再根据条件设置一下RootBeanDefinition参数的属性synthetic,最后执行真正获取实例的方法getObjectFromFactoryBean:

boolean synthetic = mbd != null && mbd.isSynthetic();
object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);

在getObjectFromFactoryBean方法中,会先根据传入参数factory和beanName的信息确定是通过单例还是多例的方式获取。

3.1 缓存获取单实例bean

如果是单例方式,利用同步代码块锁住接下来要执行代码,然后从facotryBean的缓存factoryBeanObjectCache中,根据beanName来获取实例信息,如果能获取到,直接返回对象。

否则,需要从factorybean中,通过getObject方法来获取对应的bean实例。

说到这里,其实按我们常理来说,无法就是通过beanName从Map中获取对象,这个对象直接就是bean实例就完了呗,为什么又要包装一层呢?通过FactoryBean的接口信息,我们可以窥见一二:

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    @Nullable
    T getObject() throws Exception;
    @Nullable
    Class<?> getObjectType();
    default boolean isSingleton() {
        return true;
    }
}

可以看到FactoryBean其实就是对bean实例的一个包装和扩展,这样对我们后续自定义bean实例有很大的帮助,作为框架来说,它考虑到的不仅仅是第一层,它要兼顾抽象和扩展的功能,这样才能更好的发挥Spring框架的自定义能力。

在获取到bean实例后,还需要对其进行一些处理,比如在在传入参数!synthetic为true的情况下,如果当前beanName存在于缓存singletonsCurrentlyInCreation,则直接返回bean实例即可。否则需要先把beanName加入缓存singletonsCurrentlyInCreation。

之后再执行一系列的bean后置处理器方法。这里需要说明的是,一般来说bean的后置处理器分为before和after,这里只执行了after,其原因应该是bean实例是从缓存中获取。

4 创建实例

如果从三级缓存中获取不到对应的sharedInstance,则进入创建实例逻辑。

下面我们分步解析:

第一步,首先判断当前beanName实例是否以多实例形式正在创建中,如果是则报错:

if (this.isPrototypeCurrentlyInCreation(beanName)) {
       throw new BeanCurrentlyInCreationException(beanName);
 }

第二步,判断当前IOC容器是否存在父容器,如果存在且当前beanName的bean定义信息在当前容器中找不到,则去父容器进行解析,解析方式就是结合现有体条件,以递归调用当前类getBean或者doGetBean的方式,来获取bean实例:

BeanFactory parentBeanFactory = this.getParentBeanFactory();
            if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
                String nameToLookup = this.originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
                }
                if (args != null) {
                    return parentBeanFactory.getBean(nameToLookup, args);
                }
                if (requiredType != null) {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                return parentBeanFactory.getBean(nameToLookup);
            }

第三步,标记当前beanName为已创建,就是把beanName加入已创建缓存alreadyCreated中:

if (!typeCheckOnly) {
   this.markBeanAsCreated(beanName);
}

4.1 解析@DependsOn注解

第四步,根据beanName,通过上文介绍的getMergedLocalBeanDefinition方法,获取当前类的定义信息,再检查beanName对应的类是否是抽象类,如果是抽象类则报错,因为抽象类不能实例化(但是它可以作为父类来完成一些子类的初始化动作)。

再通过获取到的bean定义信息获取当前bean的依赖信息。

具体来说就是获取当前bean中带有@DependsOn注解的方法。

这里说下@DependsOn的作用,这个注解一般要配合@Component和@Bean注解才行,意思就是在初始化需要注入的bean之前,需要先初始化@DependsOn中的bean。

光说理论,总是有点不好理解,举个例子:

@Configuration(
    proxyBeanMethods = false
)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    ......
    @Bean
    @DependsOn({"springSecurityFilterChain"})
    public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
        return this.webSecurity.getExpressionHandler();
    }
    @Bean(
        name = {"springSecurityFilterChain"}
    )
    public Filter springSecurityFilterChain() throws Exception {
    ......

就是说想要在webSecurityExpressionHandler方法中注入实例,需要下面的springSecurityFilterChain方法中的实例化先完成。

这个在我们日常开发中,还是很有用处的,可以任意的控制多个bean的初始化顺序。

在获取到@DependsOn的信息后,由于一个bean可能有多个需要在它之前实例化的bean,所以@DependsOn的value其实是一个数组,可以写入多个实例,遇到这样的情况,就需要获取@DependsOn注解的值,然后遍历处理了。

在遍历的过程中,大致又分为如下几个步骤:

4.1.1 @DependsOn循环依赖问题

//判断是否循环依赖
if (this.isDependent(beanName, dep)) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
   }
    //beanName和@DependsOn依赖bean注入到缓存中
    this.registerDependentBean(dep, beanName);
    try {
       //先创建@DependsOn依赖
       this.getBean(dep);

我们知道,对于类似@Autowire的注解,如果发送循环依赖,是在前面通过三级缓存来解决的,但是@DependsOn注解是不支持循环依赖的。

其主要表现为,满足一定的条件就会报错。比如第一种情况:

@SpringBootApplication
public class BookstoreApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookstoreApplication.class, args);
    }
    @Bean
    @DependsOn("testDepend2")
    public Object testDepend(){
        return "1";
    }
    @Bean
    @DependsOn("testDepend")
    public Object testDepend2(){
        return "2";
    }

其真正的运行流程是这样的,首先容器会实例化testDepend(beanName),但是由于其DependsOn了testDepend2(dep)。

第一步,先通过isDependent(beanName, dep)方法判断容器中的缓存dependentBeanMap中,是否有key为testDepend(beanName),此时testDepend还没有初始化一些信息,dependentBeanMap缓存当然没有它的信息,所以返回为false,这样isDependent便不会抛出异常,进入第二步。

第二步,通过registerDependentBean(dep, beanName)方法,来完成把testDepend(beanName)和依赖testDepend2(dep)分别加入缓存dependenciesForBeanMap和dependentBeanMap中,比如dependentBeanMap此时的key=testDepend2(dep),value=testDepend(beanName),而dependenciesForBeanMap的key=testDepend(beanName),value=testDepend2(dep),两者正好相反。

第三步,调用this.getBean(dep)方法,实例化testDepend2(dep)这个方法。

之后就是递归了。

此时又会重新进入到第一步的isDependent(beanName, dep)方法,但是此时的beanName便变成了testDepend2而dep换成了testDepend,所以此时还是去缓存dependentBeanMap中取,但是key却变成了testDepend2(beanName),由于在上面第二步中把还是dep的testDepend2作为key加入了缓存dependentBeanMap,所以此时能够从dependentBeanMap取到值,返回true。就抛出了循环依赖异常。

解释起来很绕,其实真正的代码更绕,有兴趣的读者可以自己去看看。

解决循环依赖的迭代依赖问题

上面解释了那么多,其实只解决了循环依赖中最简单的问题,都在一个类中,且互相依赖。

那如果换一种场景,比如testDepend依赖于testDepend2,testDepend2依赖于testDepend3,而testDepend3才最终依赖了testDepend,也就是说经过了一层中间层后,又出现了循环依赖,往往这种情况更加隐蔽。举个例子说明:

@SpringBootApplication
public class BookstoreApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookstoreApplication.class, args);
    }

    @Bean
    @DependsOn("testDepend2")
    public Object testDepend(){
        return "1";
    }
}

@Configuration
public class TestDependConfig {

    @Bean
    @DependsOn("testDepend3")
    public Object testDepend2(){
        return "2";
    }
}

@Component
public class TestDependIterate {
    @Bean
    @DependsOn("testDepend")
    public Object testDepend3(){
        return "3";
    }
}

针对这种场景,Spring使用缓存加递归的思路,还是在isDependent这个方法。

上面这个间接循环依赖的场景其实需要递归两次来执行DependsOn的bean的初始化,第一次执行testDepend的时候,isDependent中的dependentBeanMap获取到的key为testDepend2的数据为空,所以返回fasle。

String canonicalName = this.canonicalName(beanName);
Set<String> dependentBeans = (Set)this.dependentBeanMap.get(canonicalName);
 if (dependentBeans == null) {
    return false;

这里说一下,如果是testDepend和testDepend2直接循环依赖的情况,会在执行第一次递归再次进入isDependent的时候,从dependentBeanMap中获取到key为testDepend2的值包含testDepend,直接报错:

 else if (dependentBeans.contains(dependentBeanName)) {
                return true;
            } 

下面继续间接循环依赖的场景,由于最先开始实例化的是testDepend2,所以我们从它开始。

这里主要还是利用了dependentBeanMap的数据结构,它的key是字符串,而value是一个Set集合。

实例化testDepend2(其依赖为testDepend3),之后在缓存中放入了("testDepend3",["testDepend2"])的数据。

进入下一步,递归实例化testDepend3(其依赖为testDepend1)的时候,会先通过isDependent去缓存取key为"testDepend3"的value,也就是集合["testDepend2"],此集合中不包含testDepend1,所以构建新的参数递归调用isDependent(testDepend2,testDepend1),所以又要通过缓存取key为”testDepend2“的集合,取不到,所以返回false,之后在缓存中放入了("testDepend1",["testDepend3"])

最后调用递归实例化testDepend1(其依赖为testDepend2),会先通过isDependent去缓存取key为"testDepend1"的value,也就是集合["testDepend3"],此集合中不包含testDepend2,所以构建新的参数递归调用isDependent(testDepend3,testDepend2),在进入方法后,又需要从缓存中获取key为"testDepend3"的集合["testDepend2"],恰好包含testDepend2,返回true,报循环依赖错误。

其具体实现代码为:

else {
        Iterator var6 = dependentBeans.iterator();
        String transitiveDependency;
        do {
            if (!var6.hasNext()) {
                return false;
            }
            transitiveDependency = (String)var6.next();
            if (alreadySeen == null) {
                alreadySeen = new HashSet();
            }
            ((Set)alreadySeen).add(beanName);
        } while(!this.isDependent(transitiveDependency, dependentBeanName, (Set)alreadySeen));
        return true;

4.2 以单例的方式创建Bean

其主要流程分为三步,创建Bean、获取单例、销毁Bean,下面分别介绍。

4.2.1 创建Bean:createBean

其主要流程分为四步,下面分步分段介绍。

第一步,获取bean对应的class信息。

RootBeanDefinition mbdToUse = mbd;
Class<?> resolvedClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
 if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    mbdToUse = new RootBeanDefinition(mbd);
    mbdToUse.setBeanClass(resolvedClass);
 }

在这一步中,会先获取传递过来的bean定义信息mbdToUse,再根据mbdToUse的信息来解析其对应的class信息,在最后如果中的mbdToUse中的属性beanClass为空,则把获取到的class信息放入其beanClass中。

其原因便在resolveBeanClass这一步中,这里解析bean定义信息的class属性有多种方式,比如我们在IOC容器初始化中,调用方法

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

来构造bean定义信息的时候,是直接把对应文件的class信息放入RootBeanDefinition对来来构造的,简单点说就是其天然的具备beanClass属性:

if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) {
   def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);

而有的bean定义信息在注入的过程中,并不能把class信息直接通过代码传递过去,例如我们配置的一个带有@Component注解的自定义监听器,它获取到的beanClass信息就是一个字符串:

@Component
public class CustomApplicationListener  implements ApplicationListener,InterfaceService  {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("执行自定义监听器方法..........................");
    }
}

从而导致它不能直接获取mbdToUse得beanClass信息返回,它必须要做的更多,简单来说就是需要利用类加载器,或者式Class.forName的方式,利用全类名来获取beanName对应的class信息, 返回后进行下一步处理。

这里需要说明一下,由于Java中存在安全管理的功能,如果JVM参数设置了

-Djava.security.manager
-Djava.security.policy=C:\work\springboot-master\src\main\resources\test.policy

代表其开启了安全管理功能,这时候需要配置对应的test.policy文件,理论上来说只能获取其有对应权限的class信息,但是由于其使用了AccessController类的native方法:

@CallerSensitive
    public static native <T> T doPrivileged(PrivilegedAction<T> action,
                                            AccessControlContext context);

,只要是在jdk配置文件java.policy

C:\Program Files\Java\jre1.8.0_251\lib\security\java.policy

或者自定义的test.policy中,配置了对应bean的获取权限,就可以利用已有的配置信息绕过了权限检查,可以加载任意的class信息。

其完整过程如下:

if (mbd.hasBeanClass()) {
        return mbd.getBeanClass();
    } else {
        return System.getSecurityManager() != null ? (Class)AccessController.doPrivileged(() -> {
            return this.doResolveBeanClass(mbd, typesToMatch);
                }, this.getAccessControlContext()) : this.doResolveBeanClass(mbd, typesToMatch);
    }

可以看到,其实所有的过程最终都是要执行doResolveBeanClass方法来利用反射执行,只是针对是否开启安全管理功能做了权限越过。

第二步,设置方法的Overload属性

mbdToUse.prepareMethodOverrides();

获取bean定义信息中的Override方法,遍历之,逐个判断其Overload属性是否需要设置为false,如果在当前bean及其父类和接口中,如果所有的方法名只有一个,那么设置当前方法的Overload为false,表明在当前类中,未被重载过。

这里其实有个非常疑惑的问题,如果是Override方法,肯定是重写的父类或者接口方法,那么通过计算得出的方法名肯定不止一个,所以这个直接设置Overload为false的条件,几乎不可能满足。

因为它要同时满足Override和Overload,那么在当前类及其父类或者接口的同名方法肯定是大于2的。

第三步,bean初始化之前,执行特定的bean后置处理器

Object beanInstance;
 try {
     beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
     if (beanInstance != null) {
         return beanInstance;
     }

 此时,可以认为还是bean初始化的准备阶段,但是如果获取到的beanInstance不为空,则会直接当作是bean的实例返回,结束bean的创建工作。

其原理是执行了一种指定类型为

InstantiationAwareBeanPostProcessor

的后置处理器,根据执行后置处理器before和after的方法返回结果来确定beanInstance返回值。

这个指定类型的接口,其实就是继承了BeanPostProcessor接口,在其上做了一定程度的扩展,在真正开始执行bean的实例化之前来执行,可以自定义是否直接返回bean实例。

由于在IOC启动中,默认只有类ConfigurationClassPostProcessor的内部类ImportAwareBeanPostProcessor通过继承的方式实现了指定的postProcessBeforeInstantiation方法,又由于其默认方法返回为空,所以实例化开始之前的bean后置处理器,可以认为没有做任何动作,直接放行到下一步去创建。

但是这不代表我们不可以创建自定义的InstantiationAwareBeanPostProcessor接口,完全可以通过自定义的形式来返回我们指定的对象信息,举个例子,比如凡是beanName带有String开头的,我们都强制返回一个字符串实例“123”,代码示例如下:

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  if (beanName.startsWith("String")) {
       return "123";
  }
  return null;
}

 如上,默认是返回null,if中是我们自定义的逻辑,只需要使当前类继承InstantiationAwareBeanPostProcessor接口,并加入Spring容器中,便可以生效了。

第四步,执行doCreateBean方法,真正开始实例化

beanInstance = this.doCreateBean(beanName, mbdToUse, args);
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;

这一步是真正的开始实例化bean,得到结果并返回。

让我们进入doCreateBean方法,逐步分析其步骤。

获取包装类BeanWrapper

首先我们需要了解一下一个类:BeanWrapper——bean的包装类,在目前要说个很明确的用途也说不明白,只能大致说下它的作用,比如我们真正要返回的其实就是一个bean的实例,但是我们不直接返回实例,在外面包装一层,添加一些额外信息,为一些别的操作做准备。

包装的代码实现其实就是利用有参构造器,把bean的实例传进去,构造一个BeanWrapper实例,返回处理。

具体的逻辑实现为,如果是创建单实例bean,先从缓存factoryBeanInstanceCache获取key为beanName的BeanWrapper实例,在获取到BeanWrapper实例后,清空对应缓存。

如果缓存中获取不对beanName对应的BeanWrapper实例,则进入创建逻辑,主要是根据beanName,bean定义信息和args参数来实例化一个bean。

if (instanceWrapper == null) {
     instanceWrapper = this.createBeanInstance(beanName, mbd, args);
 }

在执行实例化之前,需要先判断bean的修饰符是否为public,否则报错。之后进入实例化逻辑,其具体实现分为三种情况,:

第一种,从bean定义信息——RootBeanDefinition中,直接获取instanceSupplier属性。通过函数接口Supplier的get方法获取bean实例:

Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
       return this.obtainromSupplier(instanceSupplier, beanName);

以SharedMetadataReaderFactoryContextInitializer类中的静态内部类CachingMetadataReaderFactoryPostProcessor为例,由于其实现了BeanDefinitionRegistryPostProcessor接口,且具有最高执行优先级,在Springboot项目启动过程中,在执行bean的后置处理器阶段,会先执行BeanDefinitionRegistry后置处理器,也就是说会执行CachingMetadataReaderFactoryPostProcessor类中的postProcessBeanDefinitionRegistry方法。

在这个方法中,我们实现了对特定的beanName定制bean的RootBeanDefinition信息,也就是在这里把beanName为

org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory

的相关信息定义到了RootBeanDefinition中,且通过eanDefinitionBuilder.genericBeanDefinition方法,放入beanName对应的bean实例:

private void register(BeanDefinitionRegistry registry) {
            BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean.class, SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean::new).getBeanDefinition();
            registry.registerBeanDefinition("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory", definition);
        }

所以可以通过RootBeanDefinition的定义信息直接获取bean实例,再通过BeanWrapperImpl包装后,返回BeanWrapper实例。

第二种,以FactoryMethod的方式实例化bean。

也还是借助了bean定义信息对象——RootBeanDefinition,通过其中属性factoryMethodName,是否有值来判断是否使用FactoryMethod方式实例化bean:

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

FactoryMethod方式实例化bean又分两种情况,第一种实例化static方法,不需要注入实例factoryBean;第二种实例化普通方法,需要注入实例factoryBean。其实就是利用了反射使用的两种情况。

分别举例说明,清晰一些,以实例化static方法为例,比如要实例化类ValidationAutoConfiguration种的静态方法methodValidationPostProcessor,会先在RootBeanDefinition的定义信息中设置其factoryMethodName属性为methodValidationPostProcessor,这时候设置其factoryBeanName属性为空,也就是factoryBean为空,后面利用Method类的invoke方法直接反射执行即可得到需要的实例:

@Bean
    @ConditionalOnMissingBean
    public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator) {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        boolean proxyTargetClass = (Boolean)environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
        processor.setProxyTargetClass(proxyTargetClass);
        processor.setValidator(validator);
        return processor;
    }

可以看到一般是用@Bean注入。

另一种,如果实例化非static方法,需要准备实例化好的factoryBean,其实也就是通过

factoryBean = this.beanFactory.getBean(factoryBeanName);

获取方法当前类的实例,再获取对应的beanName实例,这里注意当前类实例并不是我们需要的beanName实例,比如当前类的实例在容器的beanName是testFactory,我们需要的实例是testMy,它们处于一个类,但是有不同的beanName,举例说明:

@Configuration(
    proxyBeanMethods = false
)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    public ProxyTransactionManagementConfiguration() {
    }
    ......
    @Bean
    @Role(2)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

就是说我们需要的beanName是transactionAttributeSource,然后设置RootBeanDefinition的属性factoryMethodName为transactionAttributeSource,设置factoryBeanName属性为ProxyTransactionManagementConfiguration,也就是factoryBean为ProxyTransactionManagementConfiguration的实例,然后通过反射的方法执行方法transactionAttributeSource,即可得到我们需要的bean实例。

上面讲的是整体思路,在实际过程中会有很多具体问题,比如存在重载的情况,方法名和factoryMethodName相等的方法可能有多个,到底选哪一个,再有就是如果factoryMethodName方法存在参数,这些参数又是如何解析的,会不会存在两个方法名称一样,参数类似,从而导致Spring无法判断出要实例化哪一个呢?

这里Spring会先看RootBeanDefinition是否直接设置了执行方法,主要是看其属性resolvedConstructorOrFactoryMethod,如果有值,根据这个属性的值去计算可用的方法参数,最后根据获取到的方法名和参数,直接利用反射来实例化对象返回。

如果bean定义信息没有设置resolvedConstructorOrFactoryMethod的值,就会通过多种方式来选取候选方法——candidates,其实就是一个Method对象的集合。

选取candidates的策略又分为两种。

第一种,先判断RootBeanDefinition的属性isFactoryMethodUnique是否为true,表示当前需要实例化的bean是否只有唯一的factoryMethod,若符合条件则从属性factoryMethodToIntrospect可以直接获取到需要执行的方法名,构建成集合candidates。由于只有唯一一个候选方法,所以直接利用反射方法实例化即可,注意这种情况下实例化的参数默认为空对象(new Object[0])。

第二种,若isFactoryMethodUnique属性为false,也就是说factoryMethod不止有一个,就需要去factoryMethod的当前类,通过反射获取到所有方法名和factoryMethodName相等的方法,且此方法是否是静态方法,需要与前文中对应。也就是说如果factoryBean为空,代表需要静态方法才可以执行method方法,这里的方法也要是静态方法才能加入候选;如果factoryBean不为空,代表需要非静态方法,这里的方法也要是非静态才能加入候选。

如果候选方法的集合candidates的size大于1,则需要遍历集合,然后筛选出符合条件的method来实例化,其筛选的具体逻辑为,遍历方法和其自带的参数,每遍历一次,根据当前方法的参数的属性,算出一个权重值,然后选出一个权重值最小的方法和参数,来进行实例化,计算权重的规则主要是看当前参数的类型是否有父类或者实现的接口,每一级父类权重值加2,每一级接口权重值加1,也就是说在实例化的参数没有继承父类或实现接口的情况下,被选中的几率大。

但是也有可能最小权重值出现在2个方法中,例如可以是方法名一样,参数个数一样,这样Spring便会报错:

Ambiguous constructor matches found 

实例化参数的选择

这里其实还有一步很重要的知识点,就是关于方法参数的注入,通过举例说明:

@Bean
    public String test123(TestConfigParams testConfigParams, ApplicationContext context){
        System.out.println("进入testConfigParams"+testConfigParams);
        System.out.println(context.getApplicationName());
        return "123";
    }

比如我们的测试beanName为test123的注入,这里的参数其实是分为两类的,一类是自定义的Bean——TestConfigParams,一类是Spring自带的——ApplicationContext,这2参数的加载是有区别的,首先参数是通过遍历来解析的,也就是说TestConfigParams完了再解析ApplicationContext。

在遍历解析的过程中,先会从一个Map集合中选择合适的实例来注入,key为class信息,vlaue一般为对应实例,会先判断需要实例化的实例的参数是否是key中class的实现,如果是满足条件,则去Map中获取value,经过一定的处理后,作为参数的实例,返回给需要实例化的方法。这一步,可以认为主要是获取Spring框架自带的一些参数。

下面就是获取我们自定义的Bean的实例了,其实就是在处理完Spring框架自带的参数逻辑后,如果依旧找不到合适的参数,则通过参数的beanName从IOC容器中获取参数的类型,然后构建参数的bean定义信息,再判断bean定义信息中的autowireCandidate属性是否为true,由于bean定义信息在初始化的过程中设置其autowireCandidate属性为true,可以认为只要能取到beanName就是默认可以找到对应的参数实例的,注意这里的参数返回实例可以有多个,找到之后返回给result集合。

既然是集合,说明同一个参数有可能找到多个实例,比如一个接口的多个实现类都注入进了容器,所以需要通过bean定义信息的primary属性来判断,到底哪一个实例才是我们真正需要参数,因为真正需要的参数只有一个,所以需要判断之后选择合适的参数实例来返回。

第三种,以构造器的方式实例化bean

以上介绍的两种实例化bean的方式,其实可以说完全是Spring框架自己设定的两种模式,之后再按照这两种模式去获取实例化bean。但是对于我们来说,其实比较熟悉的创建实例方式,就应该是直接new一个对象的构造器获取实例就完了。

也就是我们将要讲到的构造器方式来实例化bean。

这种方式又分为两种情况,第一种无参构造器,没啥好说的直接选择一种合适的实例化策略,最后再通过反射来实例化对象即可。这里的实例化策略有cglib和jdk两种动态代理方式,根据bean信息的不同来选择合适的策略。

这里重点在于有参构造器情况下来实例化bean,注意这里的执行方式和上文介绍的以FactoryMethod的方式实例化bean,有异曲同工之妙,再说直白点就是很多地方源代码都写的一模一样,比如遍历解析参数,计算参数的权重值、以及一个参数取到多个值后的异常抛出,代码基本都一样,Spring的源码有时候也很让人费解,很多地方都直接copy着写,都不用封装。

唯一和FactoryMethod的方式实例化bean不同的是,前者是以bean定义信息种指定的factoryMethodName来指定类中的方法来执行实例化的,后者是通过bean定义信息中的resolvedConstructorOrFactoryMethod和resolvedConstructorArguments属性来构造器和对应的方法,在解析、比对构造器和参数后,返回合适的实例。

其具体实现在ConstructorResolver类中的方法autowireConstructor,有兴趣的读者可以去看看,和上面介绍的同一个类中的instantiateUsingFactoryMethod方法比对着看,相信会有所收获。

第五步,执行MergedBeanDefinition后置处理器

在创建完beanName的包装实例后,获取bean实例对象和实例对象的class信息,再根据class信息、bean定义信息、beanName三个参数的信息,来执行合并bean定义信息的后置处理器,在执行完成后把bean定义中的postProcessed属性标记为true,代表已经成功执行了这个后置处理器:

Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }
        synchronized(mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                } catch (Throwable var17) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);
                }
                mbd.postProcessed = true;
            }
        }

这里以后置处理器ApplicationListenerDetector为例,其主要作用是如果beanType是接口ApplicationListener的实现类,把beanName作为属性加入到当前处理器的一个Map中:

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (ApplicationListener.class.isAssignableFrom(beanType)) {
            this.singletonNames.put(beanName, beanDefinition.isSingleton());
        }
    }

第六步,执行SmartInstantiationAwareBean后置处理器后,bean实例放入三级缓存

如果当前实例对象是单例,允许循环依赖,且在Spring正在创建bean的集合中,就会去执行SmartInstantiationAwareBeanPostProcessor类型的后置处理器方法,得到增强的bean实例后,放入三级缓存中:

boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
        if (earlySingletonExposure) {
            ......
            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }

注意一般的SmartInstantiationAwareBeanPostProcessor后置处理器就是执行此接口的default方法——getEarlyBeanReference,不做处理直接返回bean,但是我们也可以自定义自己后置处理器。

再有就是在addSingletonFactory其实就是把bean实例放入了三级缓存中,但是注意,此时的bean仅仅是刚刚实例化完成,还有很多工作没有完成,例如依赖注入和bean的一些初始化需要执行的方法,但是放入了三级缓存,意味着我们可以通过前文DefaultSingletonBeanRegistry类中的getSingleton方法来直接从三级缓存获取bean实例,避免了重新创建bean。

第七步,执行populateBean方法,解析bean的注入依赖

Object exposedObject = bean;
 try {
     this.populateBean(beanName, mbd, instanceWrapper);

根据beanName,bean定义信息,包装后的bean实例,解析出bean实例的依赖属性。

这里面具体又分为几步。

第一步,执行实例化后后置处理器——InstantiationAwareBeanPostProcessor。感觉就是bean的实例化每进行一步,都有对应的后置处理器做对应的事情,这里后置处理器默认返回true,代表可以继续执行后面对bean的依赖进行注入,如果返回false,则直接结束bean的实例化过程,注意这里说的实例化和前面bean的对象实例化不是一个,可以认为前面的实例化过程并不完全,还有一些bean的属性需要注入,而且还有一些bean的初始化方法要装配。

if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
         Iterator var4 = this.getBeanPostProcessors().iterator();
         while(var4.hasNext()) {
             BeanPostProcessor bp = (BeanPostProcessor)var4.next();
             if (bp instanceof InstantiationAwareBeanPostProcessor) {
                 InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;
                 if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                      return;
......

可以看到,要是实例化后后置处理器的特定方法postProcessAfterInstantiation返回false,bean的实例化就直接结束了。这里对我们也有启示,比如我想快速的实例化一个bean,并不想它有依赖注入或者初始化方法,可以实现这个后置处理器接口,直接返回false,加快容器的启动过程。

第二步,在@Bean注解中,以autowire的形式注入依赖。

这种形式的依赖注入Spring其实已经不推荐使用了,在这里我们简要说一下。

我们知道现在常用的依赖注入形式是这样的:

   @Bean
    public String test123(TestConfigParams testConfigParams, ApplicationContext context){
        System.out.println("进入testConfigParams"+testConfigParams);
        System.out.println(context.getId());
        return "123";
    }

 在这里,我们可以直接获取容器中自定义类TestConfigParams和IOC容器类ApplicationContext的依赖,简单来说就是可以通过getBean获取容器中已经存在的实例。

但是如果@Bean注解加入了autowire属性,在其注解的方法上就只能通过空参构造器来实例化beanName为test123的实例,具体示例为beanName——testAutowired :

@Bean(autowire = Autowire.BY_NAME)
    public TestAutowired testAutowired (){
        return new TestAutowired();
    }

那么它也想要注入依赖属性怎么办,通过处理TestAutowired类中的set方法,来注入依赖:

public class TestAutowired {
    private TestConfiguration testConfiguration;

    public void test(){
        System.out.println("初始化执行方法test。。。。。");
    }

    public TestConfiguration getTestConfiguration() {
        return testConfiguration;
    }

    public void setTestConfiguration(TestConfiguration testConfiguration) {
        this.testConfiguration = testConfiguration;
    }
}

 在执行的populateBean方法的过程中,会通过如下方法,以ByName或者ByType的形式,来具体执行依赖的注入:

PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null;
     int resolvedAutowireMode = mbd.getResolvedAutowireMode();
     if (resolvedAutowireMode == 1 || resolvedAutowireMode == 2) {
         MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);
         if (resolvedAutowireMode == 1) {
             this.autowireByName(beanName, mbd, bw, newPvs);
         }
         if (resolvedAutowireMode == 2) {
             this.autowireByType(beanName, mbd, bw, newPvs);
         }
         pvs = newPvs;
     }

下面分别介绍一下具体执行流程:

autowireByName方式

通过bean定义信息和创建好的bean的包装类,获取到需要被注入的beanName,其实就是解析上文setTestConfiguration方法的信息,获取到testConfiguration,然后通过genBean方法从容器中创建或者获取其对应的bean实例。

同时把beanName为testConfiguration和testAutowired的信息,放入IOC容器的依赖Bean缓存,和被依赖bean缓存中,可以理解一个是主Bean——testAutowired,属于被依赖注入的对象,放入dependenciesForBeanMap缓存中,另一个是需要依赖注入的对象,放入dependentBeanMap中。

autowireByType方式

这种方式,相对于byName的方式,稍微麻烦一些,byName可以理解为直接是id查找,有就有,没有就基于beanName创建。而通过type查找主要是借助class的全类名进行搜索,就麻烦些了,由于满足相同全类名的bean实例,可能不止一个,那我们就需要先把这些候选的bean实例都找出来,然后再选择一个合适的bean实例来作为返回值。

这时候,如果容器中出现了多个TestConfiguration类型的beanName,还需要通过@Primary或者@Qualifier注解,来指定实际需要加载的bean实例:

    @Bean
    @Primary
    public TestConfiguration testConfiguration(){
        TestConfiguration testConfiguration = new TestConfiguration();
        System.out.println("@Bean加载");
        return testConfiguration;
    }

    @Bean
    public TestConfiguration testConfiguration1(){
        TestConfiguration testConfiguration = new TestConfiguration();
        System.out.println("@Bean1加载");
        return testConfiguration;
    }

代表加载的是第一个bean实例——testConfiguration。

目前,Spring已经不建议使用以这种方式来注入依赖了。笔者猜想,其主要原因,是因为使用太过繁琐,比较目前直接使用@Bean注解,加有参构造器注入依赖的方式,比byName和byType的简单明了的多,而达成的效果也没有本质的区别:

   @Bean
    public String test123(TestConfigParams testConfigParams, ApplicationContext context){
        System.out.println("进入testConfigParams"+testConfigParams);
        System.out.println(context.getId());
        return "123";
    }

第三步,解析需要被注入当前Bean中的属性。

其实是对第二步的补充,在这一步中,主要是针对以Field属性方式存在的依赖的注入。具体来说就是获取标记了@Autowired、@Inject、@Value等注解的对应实例,以及在后置处理器阶段配置的指定Filed属性依赖,最后全部放入集合PropertyValues中

分别举一个例子来说明,例如标记了注入注解的

@Configuration
public class TestConfiguration {
    @Inject
    private TestConfigParams testConfigParams;

这里和populateBean方法执行之前的一个后置处理器MergedBeanDefinitionPostProcessor有关,在这一步中,会通过一个指定的后置处理器AutowiredAnnotationBeanPostProcessor,来找到当前bean中所有可能需要注入属性的class信息,然后放入到InjectionMetadata类的缓存checkedElements中。

之后,当执行到populateBean方法的后置处理器InstantiationAwareBeanPostProcessor的时候,也是通过后置处理器AutowiredAnnotationBeanPostProcessor中的另一个指定方法,来对上一步后置处理器获取的注解信息进行注入:

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
            return pvs;
        } catch (BeanCreationException var6) {
            throw var6;
        } catch (Throwable var7) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
        }
    }

其代码具体表现为,通过findAutowiringMetadata方法,找到对应beanName的需要注入的metadata信息,就是上一步的checkedElements缓存,然后把metadata信息注入到bean实例的一个属性中,从而完成对@Autowired、@Inject、@Value等注解的解析,这里需要注意的一点是,在注解情况下,通常来说PropertyValues一直都是空,通过metadata获取的注解依赖实例,以filed的形式,放入了当前bean实例当中。

通过metadata获取的注解依赖实例又是如何实现的呢?

metadata.inject方法,最终会通过调用类DefaultListableBeanFactory中的方法findAutowireCandidates来获取的,前文我们提到过,这个方法主要是从IOC容器中,根据beanName和对应的class信息,来获取IOC容器中的实例。

如果是类似于ApplicationContext等基础类信息,则优先返回,如果找不到, 后面再通过beanFactory的getBean方法来获取实例 。

这样就完成了把需要依赖注入的实例,以filed的形式放入被依赖注入的实例bean的属性中。

举例说明就是,把TestConfigParams的实例,放入TestConfiguration实例的testConfigParams属性中。

注意这里的实例bean——TestConfiguration实例,是经过包装后的实例bean。

这里@Autowired、@Inject是同样的效果,其原因为:

public AutowiredAnnotationBeanPostProcessor() {
        this.autowiredAnnotationTypes.add(Autowired.class);
        this.autowiredAnnotationTypes.add(Value.class);
        try {
            this.autowiredAnnotationTypes.add(ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
            this.logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
        } catch (ClassNotFoundException var2) {
        }

    }

可以看到@Inject注解是为了支持JSR-330标准。

还有没有标记注解的,例如在类ConfigurationClassPostProcessor中,需要注入属性metadataReaderFactory,这就有一个问题了,这个属性有何特殊,为什么不注入其他和它平级的属性,偏偏注入它?其原因如下:

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR = new FullyQualifiedAnnotationBeanNameGenerator();
    ......
    private ResourceLoader resourceLoader = new DefaultResourceLoader();
    @Nullable
    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
    ......

ConfigurationClassPostProcessor是在IOC容器初始化阶段,利用AnnotationConfigUtils类,把bean定义信息注册到容器中的一个用于项目启动的beanFactory后置处理器,最初是没有注入任何属性的:

if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) {
            def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));
        }

仅仅是初始化了一些基本的bean定义信息,然后放入到容器注册器缓存中。

在通过执行到通过PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法,来执行各种beanFactory后置处理器的过程中,当执行到类SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor中的指定方法时,手动的把metadataReaderFactory的实例注入到了容器中:

private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry registry) {
     try {
        BeanDefinition definition = registry.getBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor");
         definition.getPropertyValues().add("metadataReaderFactory", new RuntimeBeanReference("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"));
     } catch (NoSuchBeanDefinitionException var3) {
    }
}

第四步,检查需要被跳过的依赖属性。

有些属性,即使被依赖进来了,也可以通过设置filter的形式,来对其进行排除,具体的排除方法为:

protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
        return AutowireUtils.isExcludedFromDependencyCheck(pd) || this.ignoredDependencyTypes.contains(pd.getPropertyType()) || AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces);
    }

第五步,把所有获取的依赖属性复制到bean实例的包装类中。

if (pvs != null) {
        this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
   }

由于我们通过BeanWrapper接口对当前beanName的实例进行了封装,但是bean定义信息中对应的一系列PropertyValues属性,和当前的Wrapper实例还是分开的,所以要通过复制的方式,把bean定义信息中的一些PropertyValues属性复制到当前的beanName包装实例中。

就这样,当前bean在通过BeanWrapper实例化后,经过populateBean方法,把一些bean的定义信息和依赖注入的属性全部复制到当前的包装后实例中,使当前bean的所有信息趋于完整,为下一步返回bean实例做好了准备。

这里又存在一个问题,BeanWrapper实例,其实是包含当前bean实例的一个超集,所以计算出来的属性,最终还是要注入到当前bean的实例属性中去,调试applyPropertyValues方法,最后会进入BeanWrapperImpl的setValue方法中,利用反射的方法来执行当前实例的set方法,给指定属性赋值:

public void setValue(@Nullable Object value) throws Exception {
            Method writeMethod = this.pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor)this.pd).getWriteMethodForActualAccess() : this.pd.getWriteMethod();
            ......
            } else {
                ReflectionUtils.makeAccessible(writeMethod);
                writeMethod.invoke(BeanWrapperImpl.this.getWrappedInstance(), value);
            }

        }

其中this.getWrappedInstance(),代表的是获取当前bean实例。

第八步,执行initializeBean方法,执行当前实例的初始化方法

exposedObject = this.initializeBean(beanName, exposedObject, mbd);

执行增强方法

进入initializeBean方法后,首先执行增强方法,如果当前bean是BeanNameAware类型的,设置beanName为当前beanName;如果当前bean是BeanClassLoaderAware类型,设置类加载器为当前IOC容器中的类加载器;如果当前bean是BeanFactoryAware类型,设置beanFactory为当前IOC容器中的beanFactory。

初始化方法之前,执行指定后置处理器

如果bean的定义信息为null,或者bean定义信息中的synthetic属性为false。执行接口BeanPostProcessor中的postProcessBeforeInitialization方法。其主要作用是对bean实例做处理。

这个后置处理器接口可以理解为是所有后置处理器的最顶层接口,所有的后置处理器,基本都可以直接使用或者重写这个后置处理器方法:

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

执行初始化方法

在执行完Before后置处理器后,进入真正的初始化方法执行,其实就是先判断,如果当前bean实现了InitializingBean接口,会在此时执行其afterPropertiesSet方法。

接着执行bean定义信息中获取的初始化方法,排除掉方法名为afterPropertiesSet的和bean定义信息中集合属性externallyManagedInitMethods的方法,之后再利用反射的方法来执行自定义的初始化方法,例如:

    @Bean(initMethod = "testInitMethod")
    public TestDependIterate getStr20(ApplicationContext hello){
        System.out.println("helloTest in HelloConfigurationInner20");
        return new TestDependIterate();
    }

public class TestDependIterate {

    public void testInitMethod(){
        System.out.println("testInitMethod");
    }
}

初始化方法之后,执行指定后置处理器

如果bean的定义信息为null,或者bean定义信息中的synthetic属性为false。执行接口BeanPostProcessor中的postProcessAfterInitialization方法。其主要作用是对bean实例做处理。

最后返回当前bean实例。

第九步,判断循环依赖是否出现异常

if (earlySingletonExposure) {
            //依次从一、二级缓存获取当前bean实例
            Object earlySingletonReference = this.getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    //如果执行完initializeBean的exposedObject没有变化
                    exposedObject = earlySingletonReference;
                } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
                    //如果执行完initializeBean的exposedObject有变化,构造异常,打印出实际bean
                    String[] dependentBeans = this.getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
                    String[] var12 = dependentBeans;
                    int var13 = dependentBeans.length;

                    for(int var14 = 0; var14 < var13; ++var14) {
                        String dependentBean = var12[var14];
                        if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

这一步,需要结合前面的关于循环依赖的代码来一起看,才能明白。

进入这一步代码,需要满足几个先决条件:

boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);

1.当前bean实例是单例

2.当前bean定义信息中允许循环依赖

3. 当前bean实例正在创建中

为了解释清楚,我们假设有实例A和实例B,这俩互相依赖。

假设IOC容器首先实例化A,那么其解决循环依赖的流程是这样的。

第一步,通过doGetBean方法中的this.getSingleton(beanName),依次从一级缓存、二级缓存、三级缓存中查找A,如果三级缓存找到A,则放入二级缓存,删除三级缓存。此时当然是找不到,略过。

第二步,在doCreateBean方法中,执行createBeanInstance方法创建bean的包装实例后,把当前的A实例对象(未包装),放入三级缓存。

第三步,在doCreateBean方法中,执行到populateBean方法,计算其依赖属性,发现B被注入到了A中,所以需要对B进行实例化后,把属性值set到A实例中。此时A的实例化过程中断。

第四步,B通过doGetBean方法中的this.getSingleton(beanName),依然找不到自身缓存,略过。进入正常的bean实例化过程。

第五步,当B也执行到populateBean方法时,又需要去解析其注入的依赖A,此时又会通过getBean>>doGetBean,又需要通过this.getSingleton(beanName)去寻找A的缓存。注意由于在第二步中,A实例化后立马放入了三级缓存,所以此时B可以直接通过三级缓存找到A,解决了B的循环依赖问题。找到A后,依然是删除三级缓存,把A放入二级缓存。此时B的实例化结束。

第六步,B的实例化结束后,放入A实例对应的属性中,此时我们需要接着第三步继续执行。

第七步,执行A实例的initializeBean方法,注意在这里,除了会执行初始化方法,最重要的是,AOP的代理对象也是在这一步生成。简单来说,经过这一步,A有可能就不是A了,它被代理劫持了。

第八步,判断循环依赖异常。也就进入我们当前需要理解的步骤,在这里首先会通过

Object earlySingletonReference = this.getSingleton(beanName, false);

中的getSingleton方法,从一二级缓存查找A实例,注意此时缓存中的A实例,是doCreateBean中仅仅经过执行createBeanInstance方法,创建包装实例后,就直接放入了缓存中的A对象,它还没有执行过populateBean和initializeBean方法。

换句话说,当前缓存中的A对象可能是一个不完整的对象,为什么说可能呢?主要是要看在initializeBean中,有没有通过代理生成新的A对象。

所以就有了接下来的判断:

if (exposedObject == bean) {

判断执行完initializeBean返回的exposedObject是否还等于createBeanInstance中生成的bean。

我们知道exposedObject在执行initializeBean之前,是直接引用的bean实例。

如果二者还相等,则直接返回二级缓存中的A实例——earlySingletonReference:

exposedObject = earlySingletonReference;

这里又有个疑问,为什么不反悔bean和exposedObject,要返回earlySingletonReference,这是因为在通过createBeanInstance生成A实例后,放入三级缓存的过程中,对实例A执行了后置处理器方法,导致放入三级缓存的A实例可能不是bean:

this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });

具体执行方法为getEarlyBeanReference,这也是Spring作为框架要考虑的多种因素。

上面说的都是正常情况。如果exposedObject != bean,那么下面就会通过beanName和找出他们的实际依赖,最后抛出异常。

第十步,如果有需要注册exposedObject到缓存中

try {
     this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
     return exposedObject;
    } catch (BeanDefinitionValidationException var16) {
     throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
    }

至此,来到最后一步,如果是单例模式,把bean实例放入到缓存disposableBeans中,如果非单例模式且不是多态模式,放入其指定的属性中。

是的,除了单例和多态模式,Spring中的Bean实例还可以是request、session、global session等几种。只不过Spring常用的基本就是单例或者多态。

这里还要注意的一点就是,这里放入的可能并不是我们真正返回的bean实例对象,因为真正返回的bean实例是exposedObject,也就是经过后置处理器处理后,可能被替换了的代理对象。

在这里把原生的bean实例放入缓存disposableBeans中,或者其余的属性中。笔者猜测,更多的是为了原始存档,方便在万一需要用到的时候,可以取到原生的bean实例。

4.3 以多态的方式创建Bean

if (mbd.isSingleton()) {
    //单例形式获取bean实例,先通过getSingleton从缓存取,取不到才执行createBean方法
	sharedInstance = this.getSingleton(beanName, () -> {
		try {
			return this.createBean(beanName, mbd, args);
		} catch (BeansException var5) {
			this.destroySingleton(beanName);
			throw var5;
		}
	});
	bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
	var11 = null;
	Object prototypeInstance;
	try {
		this.beforePrototypeCreation(beanName);
        //多态形式获取bean实例,直接执行createBean方法
		prototypeInstance = this.createBean(beanName, mbd, args);
	} finally {
		this.afterPrototypeCreation(beanName);
	}

	bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

可以看到多态形式的bean实例创建和单实例bean的创建,最大的区别还是单实例会先从三级缓存中根据beanName去取bean实例,如果取到了就直接返回,取不到才通过函数式接口创建bean实例。

而多态除了做一些准备工作,直接就开始创建实例。当然在创建过程中也不会把bean实例放入缓存中。

可以认为,这就是多态和单例bean最大的区别。

4.4 以其他的方式创建Bean

也就是以单例、多态之外的方式创建bean,这里其实与多态方式创建bean大同小异,唯一不同之处在于,和单例有一点类似,它会先通过Scope接口的get方法来获取bean实例:

public interface Scope {
    Object get(String var1, ObjectFactory<?> var2);

如果获取到了直接返回,如果获取不到,也会执行一个函数式接口来直接调用createBean方法。

Object scopedInstance = scope.get(beanName, () -> {
	this.beforePrototypeCreation(beanName);
	Object var4;
	try {
		var4 = this.createBean(beanName, mbd, args);
......

4.5 判断生成bean实例是否与requiredType兼容

if (requiredType != null && !requiredType.isInstance(bean)) {
	try {
		T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
		if (convertedBean == null) {
			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
		} else {
			return convertedBean;
		}
	} ......
		throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
	}
} else {
	return bean;
}

如果getBean方法中带有requiredType参数:

public <T> T getBean(String name, Class<T> requiredType)

首先,主要通过isInstance来判断当前实例是否是requiredType的子类或者继承类,如果满足提交直接返回bean实例对象。

否则,需要通过框架自带的类型转换器TypeConverter,来把bean实例转换为requiredType类型的对象,如果转换成功则返回转换好的bean实例,否则,抛出BeanNotOfRequiredTypeException异常。

5 总结

至此,对于bean实例化过程的getBean方法的分析,终于完成了。

在平时的开发中,其实我们对getBean,doCreateBean、pupolateBean、initializeBean...这些方法名称,想必一点也不陌生,因为在我们的项目启动过程中,如果遇到异常,在异常栈打印的过程中,是必然会出现这些方法名的。

但是实际上,扪心自问,对Bean实例的生成过程,一直都处在了解,又不十分了解的情况下。

在遇到一连串的相关异常日志时,心里难免会有点发虚。

所以有了这一次,彻底的源码解析,深入到每一行代码,每一个重难点领域,逐个解析,才算是真正对bean的实例化过程有了一个深入了解。

然后在解析的过程中,其实对实际的开发也是有很大的帮助,自我感觉是,使用一些Spring的注解配置,不再有疑惑的感觉,能够真正做到信手拈来、从容不迫。

此刻,突然对古人的一句话有了深刻理解——明不至则疑生,很符合之前的状态。

囿于笔者能力水平优先,有不足之处,还望指正!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值