16--Spring创建Bean的准备工作(一),从缓存中获取单例bean

版权声明:如有转载,请标明出处,谢谢合作! https://blog.csdn.net/lyc_liyanchao/article/details/82493058

在前几个小节中介绍了,Spring的简介,SpringIoC容器初始化,Bean的生命周期,Spring注入的三种方式,BeanFactory和FactoryBean的区别,BeanPostProcessor和BeanFactoryPostProcessor的区别,从今天开始我们就要正式开始分析Spring Bean的加载过程,从头分析Spring的bean是如何从xml配置文件,一步一步的被实例化!

1. 从缓存中获取bean

对于单例bean,Sring在创建其实例后会进行缓存,以供下次使用,所以当我们向容器索要bean的时候,第一步就是尝试从缓存中获取bean的实例,如果没有获取到,再去创建新的bean的实例,那么本小节就先从缓存中获取bean开始讲起。

12–Spring BeanFactory和FactoryBean的区别小节中,我们已经简介了BeanFactory和FactoryBean的区别,但是没有从源码的角度讲解,那么在本小节我们会从源码分析Spirng是如何获取BeanFactory和FactoryBean的缓存的。

2.Spring中的缓存对象简介

我们知道对于单例bean,Spring在创建bean之后都会缓存bean的实例,以供下次使用,那么这些bean被缓存到哪里了呢?打开DefaultSingletonBeanRegistry,可以发现该类中有很多Map和Set的缓存,这些缓存都代表什么呢?看下源码:

/** Cache of singleton objects: bean name to bean instance. */
/** 缓存beanName和bean实例 key-->beanName,value-->beanInstance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
/** 缓存beanName和beanFactory key-->beanName,value-->beanFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
/** 缓存beanName和bean实例 key-->beanName,value-->beanInstance 该缓存主要为了解决bean的循环依赖引用 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Set of registered singletons, containing the bean names in registration order. */
/** 缓存所有注册的单例beanName */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

以上代码片段摘取了比较重要的缓存定义:

  • singletonObjects:缓存beanName和bean实例 key–>beanName,value–>beanInstance
  • singletonFactories:缓存beanName和beanFactory key–>beanName,value–>beanFactory
  • earlySingletonObjects:缓存beanName和bean实例 key–>beanName,value–>beanInstance 该缓存主要为了解决bean的循环依赖引用
  • registeredSingletons:缓存所有注册的单例beanName

其他的对象暂时先不做介绍了,因为以上这些对象,我们将会在本小节中用到,大家先有所了解。

3.创建测试
  • 新建bean,Animal接口,Dog,Cat,DogFactoryBean实现类,其中DogFactoryBean还实现了FactoryBean接口,我们将Cat作为一个普通bean,将Dog作为DogFactoryBean
package com.lyc.cn.day07;

public interface Animal {
    void sayHello();
}
package com.lyc.cn.day07;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:36
 */
public class Cat implements Animal {

    private String name;

    @Override
    public void sayHello() {
        System.out.println("大家好,我是一只名叫" + getName() + "的猫,我是一个普通的bean");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.lyc.cn.day07;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:36
 */
public class Dog implements Animal {
    private String name;

    @Override
    public void sayHello() {
        System.out.println("大家好,我是一只名叫" + getName() + "的狗,我是一个通过FactoryBean的getObject()方法创建的bean");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.lyc.cn.day07;

import org.springframework.beans.factory.FactoryBean;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:41
 */
public class DogFactoryBean implements FactoryBean<Dog>, Animal {

    private String name;

    @Override
    public Dog getObject() throws Exception {
        Dog dog = new Dog();
        dog.setName(this.name);
        return dog;
    }

    @Override
    public Class<?> getObjectType() {
        return Dog.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void sayHello() {
        System.out.println("大家好,我是一只名叫" + getName() + "的狗,我是FactoryBean的实例");
    }
}
  • 新建day07.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--定义FactoryBean-->
<bean id="dog" class="com.lyc.cn.day07.DogFactoryBean" p:name="壮壮"/>

<!--定义普通bean-->
<bean id="cat" class="com.lyc.cn.day07.Cat" p:name="美美"/>

</beans>
  • 新建MyTest测试用例
package com.lyc.cn.day07;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:43
 */
public class MyTest {
    private XmlBeanFactory xmlBeanFactory;

    @Before
    public void initXmlBeanFactory() {
        System.out.println("========测试方法开始=======\n");
        xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("day07.xml"));
    }

    @After
    public void after() {
        System.out.println("\n========测试方法结束=======");
    }

    @Test
    public void test() throws Exception {
        // ① 获取两次dog,以将其缓存
        Animal dog = xmlBeanFactory.getBean("dog", Dog.class);
        dog = xmlBeanFactory.getBean("dog", Dog.class);
        dog.sayHello();

        // ② 获取两次&dog,以将其缓存
        DogFactoryBean dogFactoryBean = xmlBeanFactory.getBean("&dog", DogFactoryBean.class);
        dogFactoryBean = xmlBeanFactory.getBean("&dog", DogFactoryBean.class);
        dogFactoryBean.sayHello();

        // ③ 获取两次cat,以将其缓存
        Animal cat = xmlBeanFactory.getBean("cat", Cat.class);
        xmlBeanFactory.getBean("cat", Cat.class);
        cat.sayHello();

    }
}
  • 执行测试
========测试方法开始=======

大家好,我是一只名叫壮壮的狗,我是一个通过FactoryBean的getObject()方法创建的bean
大家好,我是一只名叫壮壮的狗,我是FactoryBean的实例
大家好,我是一只名叫美美的猫,我是一个普通的bean

========测试方法结束=======
2.beanName转换

接下来我们来分析从缓存中获取bean的过程,打开测试用例,debug跟踪代码到doGetBean()方法,transformedBeanName(name)方法就是对beanName的转换

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    //转换bean的名称,去掉&前缀,且如果bean有别名的话,优先使用别名
    final String beanName = transformedBeanName(name);
    Object bean;

    // 从缓存中获取bean
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        //省略源码中的日志打印信息
        //获取给定beanName的实例对象(对于FactoryBean,可以是bean实例本身,也可以是它创建的对象。)
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
}

转换的过程分为两步:去掉FactoryBean引用前缀,即&符;确定原始名称,将别名解析为规范名称。

  • 去掉FACTORY_BEAN_PREFIX前缀,打开BeanFactoryUtils.transformedBeanName(name)方法
/**
 * 返回bean的真实名称,去掉FactoryBean引用前缀
 * Return the actual bean name, stripping out the factory dereference
 * prefix (if any, also stripping repeated factory prefixes if found).
 * @param name the name of the bean
 * @return the transformed name
 * @see BeanFactory#FACTORY_BEAN_PREFIX
 */
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    String beanName = name;
    //beanName前缀为&,循环截取直至所有&被去掉
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}
  • 确定原始名称,将别名解析为规范名称。打开canonicalName(BeanFactoryUtils.transformedBeanName(name))方法
/**
 * 确定原始名称,将别名解析为规范名称。
 * Determine the raw name, resolving aliases to canonical names.
 * @param name the user-specified name 用户指定的beanName
 * @return the transformed name 转换后的beanName
 */
public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    do {
        //从别名缓存Map中获取对应beanName
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

以上代码还是比较简单的,对于&符前缀的理解,大家可以参考12–Spring BeanFactory和FactoryBean的区别

3.从缓存中获取Bean,打开getSingleton(beanName)方法并进入其子函数中
/**
 * 返回在给定名称下注册的(原始)单例对象。
 * 检查已经实例化的单例,并允许对当前创建的单例的早期引用(解决循环引用)。
 * @param beanName 要查找的bean的名称
 * @param allowEarlyReference 是否应该创建早期引用
 * @return 返回已经注册单例bean,如果未获取到则返回null
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从缓存中获取bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 未能获取到bean,但是允许对当前创建的单例的早期引用(解决循环引用)
    // isSingletonCurrentlyInCreation-->判断指定的单例bean是否当前正在创建(Spring只解决单例bean的循环依赖问题)
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //从earlySingletonObjects获取提前曝光的bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            //未能获取到提前曝光的bean且当前的bean允许被创建早期依赖
            if (singletonObject == null && allowEarlyReference) {
                //从缓存中获取BeanFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //通过getObject()方法获取bean,注意:通过此方法获取的bean不是被缓存的
                    singletonObject = singletonFactory.getObject();
                    //将获取到的singletonObject缓存至earlySingletonObjects
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    //从singletonFactories移除bean
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

这一部分代码又涉及到了Spring中的bean循环依赖问题,在接下来的章节中会对此问题进行详细的讲解,这里大家可以不必理会,只需要记住从singletonObjects(缓存beanName和bean实例 key–>beanName,value–>beanInstance)获取缓存的bean即可。

4.获取给定beanName的实例对象

进入到getObjectForBeanInstance(sharedInstance, name, beanName, null);方法中,查看代码

/**
 * 获取给定bean实例的对象,对于FactoryBean,可以是bean实例本身,也可以是它创建的对象。
 * @param beanInstance  共享bean实例
 * @param name 可能包含工厂取消引用前缀的名称,通过getBean()方法时传递的原始beanName
 * @param beanName 规范bean名称
 * @param mbd 合并bean定义
 * @return the object to expose for the bean
 */
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    // 判断bean是否factoryBean
    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // 当前bean是factoryBean,且beanInstance是NullBean的实例,则返回beanInstance
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // 当前bean是factoryBean,但是不是FactoryBean的实例,则抛出异常
        // 因BeanFactoryUtils.isFactoryDereference(name)-->只是从bean名称上进行了判断,我们通过getBean("&myBean")可以人为将一个非factoryBean当做factoryBean
        // 所以这里必须要判断beanInstance是否为FactoryBean的实例
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }
    }

    /**
     * 现在我们有了bean实例,它可能是一个普通的bean,也可能是一个FactoryBean。
     * 如果它是FactoryBean,我们使用它创建一个bean实例,除非调用者实际上需要工厂的引用。
     * 下面这句话稍微有些绕,首先判断beanInstance是FactoryBean的实例,然后又加了一个非的条件,将判断结果反置
     * 再加一个或条件,判断该bean的name是否有&引用,这样一来就可以判断是返回bean的实例还是返回FactoryBean对象
     * 例1:我们通过getBean("&myBean"),假设myBean实现了BeanFactory接口,那么myBean肯定是FactoryBean的实例
     * 此时将第一个判断条件置否,再去判断bean的name是否包含了&符,如果是的话,那么就返回FactoryBean对象本身
     *
     * 例2:我们通过getBean("myBean"),假设myBean是一个普通的bean,那么它肯定不是FactoryBean的实例,
     * 那么该bean跟FactoryBean无任何关系,直接返回其实例即可
     */
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    // 如果beanDefinition为null,则尝试从缓存中获取给定的FactoryBean公开的对象
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    // 未能从缓存中获得FactoryBean公开的对象,则说明该bean是一个新创建的bean
    // 下面的代码与缓存获取无关了,大家可以先不看。
    if (object == null) {
        // Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // rootBeanDefinition为null,但是在beanDefinitionMap中缓存了对应的beanName
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            //合并beanDefinition(包括父类bean)
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // 如果beanDefinition不为null,则要判断该beanDefinition对象是否通过合成获得,
        // 如果不是,则说明该beanDefinition不由有程序本身定义的
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 从给定的FactoryBean中获取指定的beanName对象
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
  • 从缓存中获取bean的过程:

    1. 解析beanName,包含了对beanName的规范和对FactoryBean的&符引用处理,如果有&会去掉。
    2. 尝试从singletonObjects缓存中获取bean,如果获取到直接返回并进一步处理。
    3. 如果未获取到,则会去判断该bean是否正在被创建(这里又涉及到了Bean的循环依赖,会在接下来的章节中详细介绍)并尝试从earlySingletonObjects获取提前曝光的bean。
    4. 如果未能获取到提前曝光的bean,尝试从BeanFactory缓存中获取指定beanName的BeanFactory。
    5. 如果可以获取到的话,则通过getObject()获取bean实例并缓存。
  • 对普通bean和FactoryBean的处理(假设已经从缓存中获取到了bean)

    1. 判断bean是否包含了对FactoryBean的前缀引用(&)。
    2. 如果有,判断bean是否NullBean实例,如果是,则直接返回。
    3. 判断bean是否FactoryBean实例,如果不是,则抛出异常。
    4. 通过if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name))判断,如果bean是FactoryBean的实例且beanName包含&符,则返回FactoryBean实例本身;如果bean不是FactoryBean的实例,则直接返回。

对于Spring缓存中获取bean就分析到这里了,希望大家可以多提意见。。。

猜你喜欢

转载自blog.csdn.net/lyc_liyanchao/article/details/82493058