【Spring源码解析】Spring XML方式获取bean过程概览

151 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1、前言

之前的文章已完成对XML资源的解析,并对解析后的对象进行加载。最后完成对bean实例的注册,也就是放入缓存。下面讲解下bean实例使用的时候是如何获取的。

这里会花多篇文章讲解,这一篇会说下获取对象的简单脉络。

2. 实例解析

这里先填写下加载bean的过程添加的实例:

public class User1 {
    private int id ;
    private String username ;
    private int age ;
    // set/get 省略 toString 方法
}

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:stu="http://www.fans.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.fans.com/schema/user http://www.fans.com/schema/stu.xsd">

    <bean id = "user" class="com.fankf.jdbc.bean.User1" name="user1">
        <property name="id" value="12"/>
        <property name="username" value="username"/>
    </bean>
<!--    <stu:user id="zs" name="zs1" email="123@123.com"/>-->
</beans>

测试代码

@Test
public void test() {
    BeanFactory context = new XmlBeanFactory(new ClassPathResource("bean6.xml"));
    User1 user = context.getBean("user", User1.class);
    System.out.println("【实例使用】"+user);
}

测试结果: 【实例使用】User1{id=12, username='username', age=0}

3、源码解析

通过以上的测试代码,我们之前已经解析完成了 BeanFactory context = new XmlBeanFactory(new ClassPathResource("bean6.xml")); 这一句,也就是 解析和注册的过程,现在User1 user = context.getBean("user", User1.class); 解析获取bean的过程。

进入源码getBean:

@Override
public Object getBean(String name, Object... args) throws BeansException {
   return doGetBean(name, null, args, false);
}

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   // 1、获取通过name获取bean真实的beanName
   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   // 2、从缓存中获取bean
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      
      // 删除部分日志提示 ... 
      
      // 3、返回对应的实例,有时候例如BeanFactory不是直接返回实例,而是返回指定方法返回的实例
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      
      // 4、判断是否是原型模式,如果存在循环依赖,直接报错
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check if bean definition exists in this factory.
      // 5、获取parentBeanFactory
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         String nameToLookup = originalBeanName(name);
         
         // 6、对parentBeanFactory 递归查询bean
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         }
         else if (args != null) {
            // Delegation to parent with explicit args.
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else if (requiredType != null) {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
         else {
            return (T) parentBeanFactory.getBean(nameToLookup);
         }
      }
      // 7、判断是否只对类型检测,那么会记录
      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      try {
          // 8、将配置Xml 配置文件的GenericBeanDefination 转换成RootBeanDefination,如果指定beanName 那么会合并到父类的相关属性
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Guarantee initialization of beans that the current bean depends on.
         // 9、如果存在依赖的bean,那么对于依赖的进行注册,并在注册完成之后先获取
         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 + "'");
               }
               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.
         // 10、获取作用域是单例的情况bean
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  // Explicitly remove instance from singleton cache: It might have been put there
                  // eagerly by the creation process, to allow for circular reference resolution.
                  // Also remove any beans that received a temporary reference to the bean.
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
         // 11、获取作用域是原型的情况bean
         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }
        // 12、处理其他作用域情况
         else {
            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;
      }
   }

   // Check if required type matches the type of the actual bean instance.
   // 13、检查需要的类型是否符合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;
}

以上就是获取bean的整体步骤,这里进行总结:

  1. 获取通过name获取bean真实的beanName
  2. 使用beanName尝试从缓存中获取bean
  3. 如果从缓存中获取到,那么返回对应的实例。
  4. 如果没有从缓存中获取,判断是否是原型模式,如果存在循环依赖,直接报错
  5. 获取parentBeanFactory
  6. 如果parentBeanFactory不为空,尝试从parentBeanFactory 递归获取bean
  7. 如果为空,那么判断是否只对类型检测,那么会记录
  8. 将配置Xml 配置文件的GenericBeanDefination 转换成RootBeanDefination,如果指定beanName 那么会合并到父类的相关属性
  9. 之后判断是否存在依赖的bean,如果存在先对依赖对象进行注册并获取
  10. 判断定义bean是否是单例模式,如果是获取作用域是单例模式的情况bean
  11. 判断定义bean是否是原型模式,如果是获取作用域是原型模式的情况bean
  12. 如果不是单例和原型模式,那么获取作用域,并获取作用域下bean
  13. 检测最终获取的bean是否和需要的bean类型一致,如果一致那么返回或的bean

以上就是获取bean的总体流程,稍后或对各个步骤进行解析。

4、总结

以上对回去bean的总体流程进行了初步探讨,稍后会进一步的解析。

每一次查看都会有收获,希望你也是。

加油!共勉!