spring 源码学习-容器初始化-1

时间:2022-01-17 05:10:58
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("application.xml")); ①
TestBean testBean = (TestBean)bf.getBean("testBean"); ②

①: spring bean 配置文件的读取

  • new ClassPathResource("application.xml") 返回一个 Resouce 的接口
    spring 源码学习-容器初始化-1
    Resource 接口抽象了Spring内部使用到的底层资源,File,Url,ClassPath等

    • 首先 定义了3个判断当前资源窗台的方法,存在性 exists() , 可读性 isReadable() , 是否处于打开状态 isOpen()
    • 获取属性 isModified(), getFilename()
    • 对应不同来源的资源有不同的实现: ByteArrayResource,DescriptiveResource,FileSystemResource,ClassRelativeContextResource,UrlResource,InputStreamResource 。
  • XmlRBeanFactory( Resource resource) 构造方法接收 resource 对象,使用XmlBeanDefinitionReader来加载Bean

    准备工作(XmlBeanDefinitionReader

    • 封装资源文件
    • 获取输入流。从Resource中获取对应的InputStream 并构造InputSource
    • 通过构造的InputSource 实例和 Resource实例调用函数 doLoadBeanDefinitions

      • 由于xml文件编码问题,第一步首先是是对资源文件进行编码处理 EncodeResource 构造出一个 EncodedResource 已编码的 Resource
      • return doLoadBeanDefinitions(inputSource, encodedResource.getResource())(核心方法) 通过InputSource 和 EncodedResource 来加载bean是读取配置文件真正的核心部分了。

        • 第一步 是通过[ inputSource,getEntityResolver(), errorHandler, validationMode, isNamespaceAware()]获取 Document 对象。(DocumentLoader

          • DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);通过 validationMode 和 namespaceAware 获得一个 DocumentBuilderFactory对象
          • DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); 通过factory,entityResolver,errorHandler 获取一个 DocumentBuilder 对象。
          • 通过 builder.parse(inputSource);得到document
        • 第二步 registerBeanDefinitions(doc, resource); 返回本次加载bean的数量

          • documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 主要关注这个方法。 方法内从doc的root节点开始遍历文档读取配置。doRegisterBeanDefinitions(root); 的源码:
            spring 源码学习-容器初始化-1
            PROFILE_ATTRIBUTE 中间部分主要在校验这个属性,xml配置中 profile 属性可以在生产环境和开发环境中配置2套不同的配置,这样方便随时切换,spring在这里是先读取的这个判断做出校验。
            preProcessXml(root);postProcessXml(root); 的代码均为空,应该是使用了模板模式,让子类可以扩展。
            parseBeanDefinitions(root, this.delegate); 的源码:
            spring 源码学习-容器初始化-1
            parseBeanDefinitions 中 会先判断 delegate.isDefaultNamespace(ele) 是否是默认命名空间(根据 element 的 BEANS_NAMESPACE_URI ),如果是就使用spring默认的读取规则,否则就使用 BeanDefinitionParserDelegate 的转换方法。2个parseDefaultElement 是完全不同的逻辑

  • BeanDefinion 是一个接口,在Spring中存在三种实现: RootBeanDefinition,ChildBeanDefinition,GenericBeanDefinition. 三种均继承自 AbstractBeanDefinition。BeanDefinition是配置文件<bean> 元素标签在容器中的内部表现形式。<bean> 中的 class, scope,lazy-init 对应 BeanDefinition中的 RootBeanDefinition是常用的实现类。GenericBeanDefinition是一站式服务类。<bean> 元素可以定义父类和子类,一般父类是 RootBeanDefinition 子类是 ChildBeanDefinition。
  • BeanDefinitionRegistry类相当于spring的bean的内存数据库,以map形式保存bean的信息

  • DefaultBeanDefinitionDocumentReader 的 parseDefaultElement 方法
    spring 源码学习-容器初始化-1
    方法对应 IMPORT_ELEMENT,ALIAS_ELEMENT, BEAN_ELEMENT, NESTED_BEANS_ELEMENT 这4种标签

BEAN_ELEMENT
spring 源码学习-容器初始化-1

  • BeanDefinitionHolder 是一个bean定义的持有对象,有private final BeanDefinition beanDefinition; 的成员和 beanName , aliases[] 别名数组 。注册bean时需要这个对象
  • delegate.parseBeanDefinitionElement(ele); 转换element 元素为 beanDefinitionHolder
    spring 源码学习-容器初始化-1

    • 获取 id 和 name 和 aliases 节点的值 ,并把id作为 beanName 的值
    • 校验 beanName 唯一性
    • AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 获取 BeanDefinition
      spring 源码学习-容器初始化-1

      • 获取类名,父类名
      • AbstractBeanDefinition bd = createBeanDefinition(className, parent); 根据类名和父类名创建一个 BeanDefinition。具体方法: BeanDefinitionReaderUtils.createBeanDefinition(
        parentName, className, this.readerContext.getBeanClassLoader());

        方法内通过 ClassUtils.forName(className, classLoader) 获取bean的类,返回GenericBeanDefinition
      • parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 中设置BeanDefine各类基本详细属性
      • parseMetaElements(ele, bd);
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        parseConstructorArgElements(ele, bd);
        parsePropertyElements(ele, bd);
        parseQualifierElements(ele, bd);
        这些根据方法名都很好理解
    • 如果没有检测到 beanName 使用默认规则为此Bean 设置 BeanName

    • 最后根据获得的 bd 返回 BeanDefinitionHolder