getbean方法找不到bean_Spring的bean的加载

这可能是全网分析bean加载流程最详细的文章了,希望对你有所帮助

上一篇文章分析了spring读取配置文件的源码,看过之后我相信你对配置文件的加载流程应该有了大概的了解,因为配置文件的解析代码并不复杂,只不过对我们来说比较生疏,因为xml解析平时开发不怎么用,我们没必要了解的太过深入,因为那并不是我们要了解的重点,真正的重点就从现在开始,那就是bean的加载。今天要分析的代码是测试类的第17行,不知道第17行的,看下上篇文章spring配置文件的解析

MyTestBean myTestBean = (MyTestBean)beanFactory.getBean("myTestBean");

这行代码在你刚接触spring的时候应该写过,当时自己写个单测,获取下bean,然后调下bean的方法,但是实际开发中并不常见,实际开发时都是用的注解比如@Resource,@Autowired直接将需要的bean进行注入,是不是你用注解了就不会用上面的getBean方法了?并不是,你用注解的话还是会调用getBean方法的,只不过是spring框架帮你调了,你只要记住,去spring容器中获取bean,必须通过getBean方法,这是唯一的入口,而BeanFactory就是spring容器的抽象,所有的容器要么是BeanFactory的子类的实现要么就是BeanFactory本身的实现。

接下来就开始分析bean的加载流程,其实上面的代码你按ctrl点击去,就会发现核心的方法叫doGetBean,这个方法就是bean加载的核心方法,咱先对这个方法中的关键代码做一个解释,然后再分析每个方法,不然如果像上篇那样一步步debug的话会有点蒙,先看下这个方法:

protected  T doGetBean(final String name, @Nullable final Class requiredType,      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        /**别名转换:你传进来的name有可能是个别名,    需要通过这个方法转换成真正的bean名称    */    final String beanName = transformedBeanName(name);    Object bean;    //从三级缓存中获取bean    Object sharedInstance = getSingleton(beanName);    //一、如果从缓存中获取到了,就直接拿来用就行了,不用再重复创建    if (sharedInstance != null && args == null) {
          if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
              /**这里判断下这个bean是否正在被创建,            如果正在被创建而且还在缓存中的话,            证明现在出现了循环依赖,为什么?            先说下什么是循环依赖:A中有个B属性,B中也有A属性,            创建A的时候发现属性B,(            这时候isSingletonCurrentlyInCreation肯定是有A的,            因为刚才说了,只有在A创建的前置处理中才把A加到了            isSingletonCurrentlyInCreation这个集合里)            于是去创建属性B,然后发现B中也有A,于是又去创建A,可此时            A正在创建了,所以这就出现了循环依赖          */          //出现了循环依赖,返回了半成品的bean,因为循环依赖中           //被依赖的Bean必须等待依赖的Bean创建完成后才能继续创建          logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +              "' that is not fully initialized yet - a consequence of a circular reference");        }else {
              //如果不是循环依赖的话,打印从缓存中获取到了实例bean          logger.debug("Returning cached instance of singleton bean '" + beanName + "'");        }      }      /**       这里的getObjectForBeanInstance方法完成的是FactoryBean的相关处理,它会根据我们传入的sharedInstance操作      如果sharedInstance不是FactoryBean的话,就直接返回我们传进来的sharedInstance;      如果是的话,就利用我们传进来的FactoryBean(也就是sharedInstance)来生成我们索要的Bean      */      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);    }else {
          //二、如果从缓存中没有获取到      /**判断是否是多例模式创建中,是的话抛出异常,多例bean创建也得按顺序        创建完一个才能创建第二个,因为创建对象的过程并不是线程安全的*/      if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);      }      //获取父类工厂      BeanFactory parentBeanFactory = getParentBeanFactory();      /**      如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition      (记得上一篇文章最后一个图吗,加载配置后,bean是存到了一个名叫      beanDefinitionMap的map里)并且父类工厂不为空,则尝试从       parentBeanFactory 中加载,还是走的这个getBean方法,      不过是父类工厂进行调用。      */      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            /**这一步是转换bean名字,点进去看这句代码做了两步:          1:别名转换,注意这里传进来的是name,不是beanName,name有可能是别名          2:然后判断如果是以&开头的,就把&拼到beanName的前面        */        String nameToLookup = originalBeanName(name);        //然后判断父类工厂是不是AbstractBeanFactory类型的        if (parentBeanFactory instanceof AbstractBeanFactory) {
              //如果是,继续调用AbstractBeanFactory的doGetBean方法          return ((AbstractBeanFactory) parentBeanFactory).doGetBean(              nameToLookup, requiredType, args, typeCheckOnly);        }else if (args != null) {
              /**判断如果传进来的参数不为空,再调的时候就把参数传进来,          BeanFactory的getBean方法是个重载方法,可以看下          */          return (T) parentBeanFactory.getBean(nameToLookup, args);        }else {
              /**          否则就只能走这个分支了,requiredType这个参数说下,          对getBean调用的时候requiredType是空的,但是可能会存在这种情况:          返回的bean其实是个String,但是requiredType却传入的是Integer,          这时候这个步骤就起作用了,所以它的功能就是将返回的bean转换成          requiredType所指定的类型          */          return parentBeanFactory.getBean(nameToLookup, requiredType);        }      }      if (!typeCheckOnly) {
            /**这个方法里涉及到两个集合:        1:alreadyCreated:这是一个set,存放已经被创建成功的bean的名称          这里面的bean至少被创建过一次        2:mergedBeanDefinitions:BeanDefinition里面包含了一个类的所有          信息,父类工厂里也会有BeanDefinition,那么子类工厂中的BeanDefinition          父类工厂中的BeanDefinition合并后就得到了RootBeanDefinition。        这个方法中判断了如果alreadyCreated这个集合里不存在这个bean的话,        就清空mergedBeanDefinitions。        */        markBeanAsCreated(beanName);      }      try {
            /**这一步就是合并父类工厂和子类工厂中的BeanDefinition        生成一个RootBeanDefinition,因为后续的操作都是基于        BeanDefinition的,所以这是必须要转的        */        final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);        //这一步就是检查这个bean是不是抽象的,抽象的话就抛异常了,因为抽象bean是不能被实例化的        checkMergedBeanDefinition(mbd, beanName, args);        //获取依赖的bean        String[] dependsOn = mbd.getDependsOn();        if (dependsOn != null) {
              //循环          for (String dep : dependsOn) {
                //判断beanName是否被dep依赖            if (isDependent(beanName, dep)) {
                  /**如果beanName所依赖的dependsOnBean,              已经存在于beanName的依赖列表里,就报错              (对于这里个人的理解是:doGetBean这个方法不是加锁的,可以              有多个线程同时操作,这里抛出异常是为了防止当前线程调用doGetBean              还没结束,其他线程再次调用doGetBean从而造成线程安全问题。)*/              throw new BeanCreationException(mbd.getResourceDescription(), beanName,                  "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");            }            //将依赖关系添加到两个map中,一个map表示依赖,一个map表示被依赖            registerDependentBean(dep, beanName);            try {
                  //获取依赖的bean,其实又去调getBean方法了,因为要生成依赖的bean              getBean(dep);            }catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,                  "'" + beanName + "' depends on missing bean '" + dep + "'", ex);            }          }        }        //判断是bean是否是单例的,其实是判断bean的scope,spring默认是singleton,也就是单例的        if (mbd.isSingleton()) {
              //创建单例bean          sharedInstance = getSingleton(beanName, () -> {
                try {
                  return createBean(beanName, mbd, args);            }            catch (BeansException ex) {
                  throw ex;            }          });          bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);        }else if (mbd.isPrototype()) {
              //如果是原生模式(多例)          Object prototypeInstance = null;          try {
                /**往prototypesCurrentlyInCreation            添加beanName标识着当前正在创建这个bean            (注意这里spring用了一个threadlocal,妙啊)            */            beforePrototypeCreation(beanName);            //创建bean            prototypeInstance = createBean(beanName, m
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值