Springframework源码解析-doGetBean源码详解

青旅半醒 2024-03-24 11:29 76阅读 0赞

1. doGetBean方法源码解析

  1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
  2. @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  3. /**
  4. * 通过name获取BeanName,这里不能使用name作为beanName:
  5. * 1. name可能是别名,通过方法转换为具体的实例名称
  6. * 2. name可能会以&开头,表明调用者想获取FactoryBean本身,而非FactoryBean创建bean
  7. * FactoryBean 的实现类和其他的 bean 存储方式是一致的,即 <beanName, bean>,
  8. * beanName 中是没有 & 这个字符的。所以我们需要将 name 的首字符 & 移除,这样才能从
  9. * 缓存里取到 FactoryBean 实例。
  10. *
  11. */
  12. final String beanName = transformedBeanName(name);
  13. Object bean;
  14. // 从缓存中获取bean
  15. Object sharedInstance = getSingleton(beanName);
  16. /*
  17. * 如果 sharedInstance = null,则说明缓存里没有对应的实例,表明这个实例还没创建。
  18. *( BeanFactory 并不会在一开始就将所有的单例 bean 实例化好,而是在调用 getBean 获取bean 时再实例化,也就是懒加载)。
  19. * getBean 方法有很多重载,比如 getBean(String name, Object... args),我们在首次获取
  20. * 某个 bean 时,可以传入用于初始化 bean 的参数数组(args),BeanFactory 会根据这些参数
  21. * 去匹配合适的构造方法构造 bean 实例。当然,如果单例 bean 早已创建好,这里的 args 就没有
  22. * 用了,BeanFactory 不会多次实例化单例 bean。
  23. */
  24. if (sharedInstance != null && args == null) {
  25. if (logger.isTraceEnabled()) {
  26. if (isSingletonCurrentlyInCreation(beanName)) {
  27. logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
  28. "' that is not fully initialized yet - a consequence of a circular reference");
  29. }
  30. else {
  31. logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
  32. }
  33. }
  34. /*
  35. * 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
  36. * sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
  37. * bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
  38. * 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
  39. */
  40. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  41. }
  42. /*
  43. * 如果上面的条件不满足,则表明 sharedInstance 可能为空,此时 beanName 对应的 bean
  44. * 实例可能还未创建。这里还存在另一种可能,如果当前容器有父容器,beanName 对应的 bean 实例
  45. * 可能是在父容器中被创建了,所以在创建实例前,需要先去父容器里检查一下。
  46. */
  47. else {
  48. // BeanFactory 不缓存 Prototype 类型的 bean,无法处理该类型 bean 的循环依赖问题
  49. //判断是否存在循环依赖
  50. if (isPrototypeCurrentlyInCreation(beanName)) {
  51. throw new BeanCurrentlyInCreationException(beanName);
  52. }
  53. // 如果 sharedInstance = null,则到父容器中查找 bean 实例
  54. BeanFactory parentBeanFactory = getParentBeanFactory();
  55. if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
  56. // Not found -> check parent.
  57. String nameToLookup = originalBeanName(name);
  58. if (parentBeanFactory instanceof AbstractBeanFactory) {
  59. return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
  60. nameToLookup, requiredType, args, typeCheckOnly);
  61. }
  62. else if (args != null) {
  63. // Delegation to parent with explicit args.
  64. return (T) parentBeanFactory.getBean(nameToLookup, args);
  65. }
  66. else if (requiredType != null) {
  67. // No args -> delegate to standard getBean method.
  68. return parentBeanFactory.getBean(nameToLookup, requiredType);
  69. }
  70. else {
  71. return (T) parentBeanFactory.getBean(nameToLookup);
  72. }
  73. }
  74. if (!typeCheckOnly) {
  75. markBeanAsCreated(beanName);
  76. }
  77. try {
  78. // 合并父 BeanDefinition 与子 BeanDefinition
  79. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  80. checkMergedBeanDefinition(mbd, beanName, args);
  81. // 检查是否有 dependsOn 依赖,如果有则先初始化所依赖的 bean
  82. String[] dependsOn = mbd.getDependsOn();
  83. if (dependsOn != null) {
  84. for (String dep : dependsOn) {
  85. /*
  86. * 检测是否存在 depends-on 循环依赖,若存在则抛异常。比如 A 依赖 B,
  87. * B 又依赖 A,他们的配置如下:
  88. * <bean id="beanA" class="BeanA" depends-on="beanB">
  89. * <bean id="beanB" class="BeanB" depends-on="beanA">
  90. *
  91. * beanA 要求 beanB 在其之前被创建,但 beanB 又要求 beanA 先于它
  92. * 创建。这个时候形成了循环,对于 depends-on 循环,Spring 会直接
  93. * 抛出异常
  94. */
  95. if (isDependent(beanName, dep)) {
  96. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  97. "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
  98. }
  99. // 注册依赖记录

发表评论

表情:
评论列表 (有 0 条评论,76人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Vue解析:虚拟dom详解

    一句话:vue的虚拟dom是将多次dom操作保存在一个js对象(虚拟dom对象)中,然后用这个js对象一次性的去更新dom操作,这样就避免了很多无效的计算 一、DOM和其解

    相关 Spring解析——如何阅读

    >   最近没什么实质性的工作,正好有点时间,就想学学别人的代码。也看过一点源码,算是有了点阅读的经验,于是下定决心看下spring这种大型的项目的源码,学学它的设计思想。 >