美文网首页
spring容器之bean的加载

spring容器之bean的加载

作者: 会上树的程序猿 | 来源:发表于2019-07-02 00:19 被阅读0次

前面所有的学习中我们完成了加载bean之前的一些的准备,从资源的定位到将资源转化为文档对象再到对文档实例中的各种标签的解析、封装、装饰等过程,最后是对beanDefinition的注册,完成了这些操做,我们接下来需要对bean的获取以及创建过程进行简单的学习,首先还是从我们最初的代码出发:

  public static void main( String[] args )
{
    System.out.println( "Hello World!" );

    XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    Object student = factory.getBean("student");
    System.out.println(student);

}

上述代码很简单,做的就是从spring的容器中获取Student,我们可以看到的是,首先我们需要一个bean的工厂然后去调用它的getBean方法这样我们可以发现就能拿到我们想要的bean实例,这里是student,在它调用getBean方法时,到底做了什么操作才是我们要学习的真正内容,直接跟踪代码进去:

AbstractBeanFactory.java
@Override
//获取bean
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

当我们调用getBean方法时,发现它的内部调用一个doGetBean方法,继续跟踪代码:

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

    //该方法去掉别名之后的bean的name,如我这里有一个Student的真实的bean,别名为 Student------>aaStudent
    //Student-------->aaaaStudent,该方法内部循环处理,最终返回Student即可
    final String beanName = transformedBeanName(name);
    Object bean;

    //从上面的去掉别名的beanName拿到后,从缓存中去拿,调用了DefaultSingletonBeanRegistry的getSingleton方法
    Object sharedInstance = getSingleton(beanName);
    //这里做判断
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        //获取bean的实例
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    //这里表明上述的sharedInstance为null
    else {
        //这里分情况来看,spring考虑到了,如果当前的bean已经被创建,获取会失败,可能有别的bean在引用该bean
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        //如果容器中没有找到,则从父类容器中加载
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            //当前类的爸爸中没找到,从爷爷辈去找,直到找到
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                //调用AbstractBeanFactory的doGetBean方法去获取bean的实例
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                        nameToLookup, requiredType, args, typeCheckOnly);
            }
            //args为首次创建bean的实例时的参数
            else if (args != null) {
                //使用代理的方式去获取bean实例
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            //bean的真实类型
            else if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            //这里表示我们的args和requiredType都为null,直接通过得到的bean的名字去获取实例
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }
        //对bean的真实类型检查,在检查之前不可使用该bean
        if (!typeCheckOnly) {
            //对已经创建完的bean的实例做标记,如:已完成创建
            markBeanAsCreated(beanName);
        }

        try {
            //合并父子bean的定义
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            //检查合并之后的bean的定义,必要时抛异常
            checkMergedBeanDefinition(mbd, beanName, args);

            //先初始化当前bean所依赖的bean
            //如:我的订单类需要商品类,此刻spring会先初始化商品类,大概就是这样的
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    //给当前bean注册一个依赖bean
                    registerDependentBean(dep, beanName);
                    try {

                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // Create bean instance.
            //创建bean实例
            //单例的情况下
            if (mbd.isSingleton()) {
                //从缓存(singletonObjects)中去拿
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        //最后从缓存中移除对应的实例原因有:
                        //1.每当有bean在创建时,允许循环参考来解析对应的beanDefinition
                        //2.删除临时引用该bean的bean
                        destroySingleton(beanName);
                        throw ex;
                    }
                });

                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            //多例的情况下:
            else if (mbd.isPrototype()) {

                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    //创建完bean的处理
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                //获取bean的作用域
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    //检查类型与实际的bean的是否匹配
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
};

上述doGetBean方法看着是做了很多事,过程也很复杂,这里进行拆分来说:

  • 首先是对beanName的获取过程
 // AbstractBeanFactory.java

final String beanName = transformedBeanName(nam?e);

该方法主要的作用对beanName的转化过程,我们这里传入的参数name可能不是beanName,有可能是alias,也可能是factoryBean类型的.接着看转化的过程:

  protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

在该方法我们看到实质上调用了内部的canonicalName方法,在该方法中需要参数是通过BeanFactoryUtils#transformedBeanName(name)获取,我们来看一下该方法做了什么:

BeanFactoryUtils.java
  /**
 * Separator for generated bean names. If a class name or parent name is not
 * unique, "#1", "#2" etc will be appended, until the name becomes unique.
 */
public static final String GENERATED_BEAN_NAME_SEPARATOR = "#";

/**
 * Cache from name with factory bean prefix to stripped name without dereference.
 * @since 5.1
 * @see BeanFactory#FACTORY_BEAN_PREFIX
 */
private static final Map<String, String> transformedBeanNameCache = new ConcurrentHashMap<>();
public static String transformedBeanName(String name) {

    //String FACTORY_BEAN_PREFIX = "&";
    Assert.notNull(name, "'name' must not be null");
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    //只要满足&的前缀,循环处理
    //调用transformedBeanNameCache#computeIfAbsent()方法通过name获取真正的beanName
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

上面的代码是对beanName的转化过程,我们可以看到的是主要是针对于FactoryBean的处理过程,假如传入的name参数为"&AA",首先是去掉'&',即name='AA',接着看:

 SimpleAliasRegistry.java

/** Map from alias to canonical name. */
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

/**
 * Determine the raw name, resolving aliases to canonical names.
 * @param name the user-specified name
 * @return the transformed name
 */
public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    //循环处理,最终获取beanName
    do {
        //获取alias
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

上述方法就是对alias进行循环处理最终获取beanName的过程,如:
别名a指向了别名b,那么返回b,若别名a指向了b且b指向了c最终返回c即可

  • 获取单例bean
//从上面的去掉别名的beanName拿到后,从缓存中去拿,调用了DefaultSingletonBeanRegistry的getSingleton方法
    Object sharedInstance = getSingleton(beanName);

我们知道单例bean的创建过程中,在同一个容器中只能被创建一次,后续获取bean直接从缓存中获取即可,关于该方法的逻辑我们在后面的学习中重点来学习.

  • 从bean中获取bean实例
if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        //获取bean的实例
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

在我们获取单例bean的基础上首先是进行了判null处理,在不为null的情况下调用isSingletonCurrentlyInCreation方法进行了循环依赖的检查,最后通过getObjectForBeanInstance方法进行获取实例的操作

DefaultSingletonBeanRegistry.java
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation =
        Collections.newSetFromMap(new ConcurrentHashMap<>(16));

/***
 * 返回指定的单例bean当前是否在创建
 * @param beanName
 * @return
 */
public boolean isSingletonCurrentlyInCreation(String beanName) {
    return this.singletonsCurrentlyInCreation.contains(beanName);
}

关于getObjectForBeanInstance方法处理过程在后续的学习中进行详细的说明,简单的提一下因为我们从缓存中获取到的bean是原始状态的bean,并未进行实例化的操作,所以获取到的bean不一定是我们想要的,因此才有了对bean实例的操作即getObjectForBeanInstance方法

  • 接下来是对原型模式的依赖检查
if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

因为spring只对单例模式下的循环依赖进行处理,这里如果是原型模式的话直接抛异常,其主要的目的是为了解决循环依赖的问题

  • 从父容器去寻找
//如果容器中没有找到,则从父类容器中加载
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            //当前类的爸爸中没找到,从爷爷辈去找,直到找到
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                //调用AbstractBeanFactory的doGetBean方法去获取bean的实例
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                        nameToLookup, requiredType, args, typeCheckOnly);
            }
            //args为首次创建bean的实例时的参数
            else if (args != null) {
                //使用代理的方式去获取bean实例
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            //bean的真实类型
            else if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            //这里表示我们的args和requiredType都为null,直接通过得到的bean的名字去获取实例
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }

这里就是一个递归寻找的过程,直到找到为止,详细的过程我们后面来说

  • 对已经完成创建了的bean进行标记
//对bean的真实类型检查,在检查之前不可使用该bean
if (!typeCheckOnly) {
//对已经创建完的bean的实例做标记,如:已完成创建
markBeanAsCreated(beanName);
}

关于标记的过程,后面的学习中详细说

  • 对beanDefinition进行合并和相应的检查处理
//合并父子bean的定义
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//检查合并之后的bean的定义
checkMergedBeanDefinition(mbd, beanName, args);

该过程主要是针对xml配置中读取到的bean信息保存在GenericBeanDefinition中,因为在后续bean的处理过程中都需要进行统一化,这里进行了合并RootBeanDefinition的操作,详细过程后面的学习过程中我们再来看

  • 针对于循环依赖bean的处理过程
//先初始化当前bean所依赖的bean
//如:我的订单类需要商品类,此刻spring会先初始化商品类,大概就是这样的
String[] dependsOn = mbd.getDependsOn();
 if (dependsOn != null) {
    for (String dep : dependsOn) {
        if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    //给当前bean注册一个依赖bean
                    registerDependentBean(dep, beanName);
                    try {
                        //递归处理
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

该过程考虑到了bean的实例化过程中不可能是单独存在的,可能需要依赖于bean作为属性,那么这个时候需要先实例化依赖的bean,关于详细的过程后面来讲.

  • 针对于不同scope的bean进行创建
//创建bean实例
//单例的情况下
if (mbd.isSingleton()) {
//从缓存(singletonObjects)中去拿
sharedInstance = getSingleton(beanName, () -> {
    try {
            return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
            //最后从缓存中移除对应的实例原因有:
              //1.每当有bean在创建时,允许循环参考来解析对应的beanDefinition
          //2.删除临时引用该bean的bean
          destroySingleton(beanName);
            throw ex;
            }
    });

    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            //原型的情况下:
            else if (mbd.isPrototype()) {

                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    //创建完bean的处理
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                //获取bean的作用域
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }

上述是针对于不同作用域的bean的创建过程,如 单例 原型 等.

  • 类型转换
//检查类型与实际的bean的是否匹配
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            // 执行转换
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }

在我们的doGetBean方法中参数requiredType的作用就是将返回的bean转换为requiredType类型的.
其次存在一种情况就是当requiredType为null时,我们返回的bean为String类型的,实质上我们需要转换为Integer类型的,这个时候该过程就不管用了,所以一般来说不需要我们手动转换类型.
到这里我们对于BeanFactory#getBean的过程分析完了,在后面的学习中对上面的步骤进行详细的分析.

相关文章

  • Spring:自定义类扫描器(扫包)

    相关文章:Spring:获取容器中的Bean相关文章:Spring Boot:容器加载时执行特定操作 前言 在我们...

  • Spring Bean加载

    Spring Bean加载 Spring Bean加载的入口: 可以看到Spring Bean加载主要在doGet...

  • SpringAOP的原理

    1、当spring容器启动的时候,加载两个bean,对两个bean进行实例化2、当spring容器对配置文件解析到...

  • spring源码

    spring如何创建bean 同样先把java类通过classloader加载到class,Spring容器一启动...

  • spring容器之bean的加载

    前面所有的学习中我们完成了加载bean之前的一些的准备,从资源的定位到将资源转化为文档对象再到对文档实例中的各种标...

  • Spring常用注解介绍【经典总结】

    Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用Spri...

  • Spring常用注解

    Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用Spri...

  • 5.Spring的常用标记和常用注解

    Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用Spri...

  • Spring 容器

    Spring 容器 创建Bean 管理Bean的生命周期 组装Bean 配置Bean Spring容器是Sprin...

  • @Component 无法注入Bean ,无法使用 @value

    一. 原因是 spring容器加载bean时机不同,导致注入bean为空 参考1: https://www.cnb...

网友评论

      本文标题:spring容器之bean的加载

      本文链接:https://www.haomeiwen.com/subject/fbedcctx.html