飞道的博客

spring源码分析容器的使用

256人阅读  评论(0)

目录

一、spring容器使用的业务场景

  1、xml配置

  2、java类

  3、输出结果

  4、总结

二、spring容器获取bean实例的流程

       1、功能分析

       2、容器获取bean实例流程概览

       3、初始对应的BeanFactory

       4、resource转换Document

       5、解析Docuemnt转换为BeanDefinition

 

//TODO 解析Document 填充BeanDefintion对象源码分析未完成

 


前言:通过写了两篇博文Spring源码分析之一BeanFactory相关spring源码分析之BeanDefinition相关两篇博文后,我们真正要探究的主题才要开始了。接下来让我们以一个常见的业务场景为例子 以XML的形式一步一步揭开spring容器的使用的神秘面纱。

一、spring容器使用的业务场景

  1、xml配置


  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- 从 xsi:schemaLocation中值得后缀可以看出次xml验证模式是xsd模式 -->
  3. <beans xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation= "http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <!-- xml的形式声明一个需要spring容器帮我们管理的bean实例 -->
  8. <bean id="worker" class="com.xiu.bean.Worker" >
  9. <property name="name" value="索隆" />
  10. </bean>
  11. </beans>

  2、java类

         


  
  1. //需要spring管理的bean
  2. public class Worker {
  3. private String name;
  4. public Worker(){
  5. System.out.println( "the worker object is finish");
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. }
  14. //测试类
  15. @Test
  16. public void testGetBean(){
  17. //创建beanFactory对象工厂
  18. XmlBeanFactory beanFactory = new XmlBeanFactory( new ClassPathResource( "spring-beans.xml"));
  19. //从工厂中获取对应的bean实例
  20. Worker worker = beanFactory.getBean( "worker", Worker.class);
  21. }

  3、输出结果

  4、总结

          总的来说我么需要分析测试方法中的两行代码,看似简单的两行但是分析起来代码很庞大,这大概就是源码分析让人又爱又恨的魔力吧。鉴于篇幅较长,两行代码一行代码一篇博文来介绍

  1. XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-beans.xml"));
  2. Worker worker = beanFactory.getBean("worker", Worker.class);

  第一行代码到底做了那些功能呢?让我们一起来探究一下吧。

二、spring容器获取bean实例的流程

       1、功能分析

                  抛开复杂细节基于我们使用spring以及测试结果,大致分析一下上面做了那些工作。

                  1、首先资源文件xml的加载和解析(需要把bean标签的一些属性和子标签解析的相关数据信息解析出来)

                  2、实例化bean这样我们才可以拿出使用

           2、容器获取bean实例流程概览

  • 初始对应的BeanFactory
  • 将对应的资源包装成便于spring统一管理的Resource
  • 将Resource实例根据不同的验证方式(DTD或者XSD) 转换为对应的Document
  • 解析Docuemnt的标签元素 包含两种解析方式 默认标签解析和自定义标签解析。

3、初始对应的BeanFactory


  
  1. //XmlBeanFactory类
  2. //调用XmlBeanFactory构造器
  3. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  4. //调用父类DefaultListableBeanFactory构造器 该类继续调用其父类AbstractAutowireCapableBeanFactory构造器
  5. super(parentBeanFactory);
  6. //将xml对应的resource 转换为BeanDefiniion并装载在Spring容器中
  7. this.reader.loadBeanDefinitions(resource);
  8. }
  9. //----------------------------------------------------------------------------------------------------//
  10. //AbstractAutowireCapableBeanFactory类
  11. //最终调用其爷爷类的AbstractAutowireCapableBeanFactory
  12. public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
  13. //执行相关的接口忽略注入
  14. this();
  15. //如果该工厂对象有父BeanFactory 则设置其父BeanFactory
  16. setParentBeanFactory(parentBeanFactory);
  17. }
  18. //this()方法
  19. public AbstractAutowireCapableBeanFactory() {
  20. //调用其父类AbstractBeanFactory 空构造器
  21. super();
  22. //设置需要忽略依赖注入的接口
  23. //正常情况下如果如果一个类实例 beanA依赖 另一个实例beanB 则在初始化beanA的时候 需要先初始化beanB
  24. //但是ignoreDependencyInterface声明忽略其对应的beanB的时候 则beanA初始化的时候不会初始化beanB
  25. //如果实例bean 实现了如下的BeanNameAware(设置BeanName)、BeanFactoryAware(设置BeanFactory)、BeanClassLoaderAware(设置BeanClassLoader)三个接口
  26. //则bean 依赖的beanName、BeanFactory、BeanClassLoader属性不会去初始化 而是spring根据bean的生命周期内将对应的属性设置进去
  27. //https://blog.csdn.net/wubai250/article/details/8255918 挺不错的
  28. ignoreDependencyInterface(BeanNameAware.class);
  29. ignoreDependencyInterface(BeanFactoryAware.class);
  30. ignoreDependencyInterface(BeanClassLoaderAware.class);
  31. }

4、resource转换Document


  
  1. //XmlBeanFactory
  2. //使用XmlBeanDefinitionReader 来实现如下功能
  3. //将xml对应的resource 转换为BeanDefiniion并装载在Spring容器中
  4. this.reader.loadBeanDefinitions(resource);
  5. //---------------------------------------------------------------------------------------------//
  6. //XmlBeanDefinitionReader
  7. //真正的资源加载并转换为BeanDefinition
  8. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  9. Assert.notNull(encodedResource, "EncodedResource must not be null");
  10. if (logger.isInfoEnabled()) {
  11. logger.info( "Loading XML bean definitions from " + encodedResource);
  12. }
  13. //获取到存在的资源列表 并添加其新加载的资源 重复加载会抛出响应异常
  14. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  15. if (currentResources == null) {
  16. currentResources = new HashSet<>( 4);
  17. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  18. }
  19. if (!currentResources.add(encodedResource)) {
  20. throw new BeanDefinitionStoreException(
  21. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  22. }
  23. try {
  24. //获取inputStrem 流对象 将其包装成适合进行xml解析的InputSource
  25. InputStream inputStream = encodedResource.getResource().getInputStream();
  26. try {
  27. InputSource inputSource = new InputSource(inputStream);
  28. if (encodedResource.getEncoding() != null) {
  29. inputSource.setEncoding(encodedResource.getEncoding());
  30. }
  31. //执行真正的转换xml为document 解析document 转换为BeanDefinition
  32. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  33. }
  34. finally {
  35. inputStream.close();
  36. }
  37. }
  38. catch (IOException ex) {
  39. throw new BeanDefinitionStoreException(
  40. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  41. }
  42. finally {
  43. currentResources.remove(encodedResource);
  44. if (currentResources.isEmpty()) {
  45. this.resourcesCurrentlyBeingLoaded.remove();
  46. }
  47. }
  48. }
  49. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  50. throws BeanDefinitionStoreException {
  51. try {
  52. //将对应资源转换为Document对象
  53. Document doc = doLoadDocument(inputSource, resource);
  54. //解析doc中的属性转换为BeanDefinition对象 并注册在spring容器中 该部分功能我们放后面讲解
  55. return registerBeanDefinitions(doc, resource);
  56. }
  57. //省略一些异常捕获处理代码
  58. catch (BeanDefinitionStoreException ex) {
  59. throw ex;
  60. }
  61. }
  62. //执行将resource资源文件转换为Document的操作
  63. protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
  64. //转换document功能不由该类本身完成 而是委托给了DoucmentLoader(其实真正实现转换Doc功能的
  65. 是其子类DefaultDoucmentLoader)
  66. //参数本身有符合xml解析的InputSource类
  67. //EntityResolver 作用提供一个解析xml的DTD/XSD声明的方法 来避免从网络上寻找声明出现的问题
  68. //对于解析XMl sax需要先读取xml文档声明 以便对其xml文档进行验证。
  69. //errorHandler错误处理类
  70. //ValidationMode xml验证模式 XSD 或者DTD
  71. //NamespaceAware 是否感知xml的命名空间
  72. //这里需要着重介绍一下其
  73. return this.documentLoader.loadDocument(inputSource, getEntityResolver(),
  74. this.errorHandler,
  75. getValidationModeForResource(resource), isNamespaceAware());
  76. }
  77. //---------------------------------------------------------------------------------------------//
  78. //DefaultDoucmentLoader类
  79. //很简单一般的解析XML的方式 获取DocumentBuilderFactory 工厂创建DocumentBuilder 调用其parse()方法 将资源Resource转换为Document。
  80. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
  81. ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
  82. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  83. if (logger.isDebugEnabled()) {
  84. logger.debug( "Using JAXP provider [" + factory.getClass().getName() + "]");
  85. }
  86. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  87. return builder.parse(inputSource);
  88. }

5、解析Docuemnt转换为BeanDefinition

在4中我们介绍了资源转换为Document的流程 在XmlBeanDefinitionReader 类的doLoadBeanDefinitions()方法


  
  1. //将对应资源转换为Document对象
  2. Document doc = doLoadDocument(inputSource, resource);
  3. //解析doc中的属性转换为BeanDefinition对象 并注册在spring容器中
  4. return registerBeanDefinitions(doc, resource);

registerBeanDefinitions(doc,resource)则对应接下来的主题解析document对象 并将其转换为BeanDefinitions()


  
  1. //XmlBeanDefinitionReader
  2. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  3. //BeanDefinitionDocumentReader 主要的作用是读取Document 并注册BeanDefinition对象
  4. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  5. int countBefore = getRegistry().getBeanDefinitionCount();
  6. //执行读取Document转换为BeanDefinition注册spring 委托给BeanDefinitionDocumentReader 来进行处理()真正实现是DefaultBeanDefinitionDocumentReader
  7. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  8. //返回BeanDefinition成功的个数
  9. return getRegistry().getBeanDefinitionCount() - countBefore;
  10. }
  11. //-------------------------------------------------------------------------------------------------------//
  12. //DefaultBeanDefinitionDocumentReader
  13. //调用DefaultBeanDefinitionDocumentReader实现类的registerBeanDefinitions
  14. @Override
  15. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  16. this.readerContext = readerContext;
  17. logger.debug( "Loading bean definitions");
  18. Element root = doc.getDocumentElement();
  19. //执行真正的核心的解析document注册BeanDefinition
  20. doRegisterBeanDefinitions(root);
  21. }
  22. protected void doRegisterBeanDefinitions(Element root) {
  23. //具体的解析document 还是委托给了BeanDefinitionParserDelegate 来进行处理
  24. BeanDefinitionParserDelegate parent = this.delegate;
  25. this.delegate = createDelegate(getReaderContext(), root, parent);
  26. if ( this.delegate.isDefaultNamespace(root)) {
  27. //获取对应的profile 从而应用profile对应环境
  28. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  29. if (StringUtils.hasText(profileSpec)) {
  30. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  31. profileSpec, BeanDefinitionParserDelegate.
  32. MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  33. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles))
  34. {
  35. if (logger.isInfoEnabled()) {
  36. logger.info( "Skipped XML bean definition file due to specified
  37. profiles [" + profileSpec + "] not matching: "
  38. + getReaderContext().getResource());
  39. }
  40. return;
  41. }
  42. }
  43. }
  44. //document解析之前的处理 空实现 如果想要在document解析之前做一些特定操作
  45. // 可以使用子类继承该类并重写该方法
  46. preProcessXml(root);
  47. //执行document解析
  48. parseBeanDefinitions(root, this.delegate);
  49. //document解析之后的处理 空实现 如果想要在document解析之后做一些特定操作
  50. //可以使用子类继承该类并重写该方法
  51. postProcessXml(root);
  52. this.delegate = parent;
  53. }
  54. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  55. //如果是xml默认的命名空间则说明xml配置的标签是类似于<bean id="" cals="" />等
  56. //如果是xml不是默认的命名空间则说明xml配置的标签是类似于<tx:annotation-driven />
  57. //等自定义标签
  58. //两者标签解析差别很对 默认的配置spring自己知道如何解析 但是对应自定义的标签
  59. // 可以需要用户实现一些相关的接口
  60. //所以单独需要使用单独的方法来进行解析
  61. if (delegate.isDefaultNamespace(root)) {
  62. NodeList nl = root.getChildNodes();
  63. for ( int i = 0; i < nl.getLength(); i++) {
  64. Node node = nl.item(i);
  65. if (node instanceof Element) {
  66. Element ele = (Element) node;
  67. if (delegate.isDefaultNamespace(ele)) {
  68. //默认标签元素解析 beans 默认字标签<import> <bean> <property>等
  69. parseDefaultElement(ele, delegate);
  70. }
  71. else {
  72. //自定义标签解析 需要结合用户实现的相关接口配合解析
  73. delegate.parseCustomElement(ele);
  74. }
  75. }
  76. }
  77. }
  78. else {
  79. //自定义标签解析 需要结合用户实现的相关接口配合解析
  80. delegate.parseCustomElement(root);
  81. }
  82. }

三、spring默认标签的解析

    在上面的逻辑中 我们大致分析了如何将xml转换为Document,对应的document转换为BeanDefinition并注册的解析逻辑,但是关于标签的详细解析留在这个环节去讲解,首先根据上面的代码分析我们可以清楚的知道有关spring标签的解析分为两类,

  • 默认标签的解析,例如:

              <bean id="son" class="" > 

               <alias />

  • 自定义标签的解析

             <tx:advice />

             <aop:config/>

     1、默认标签解析分析

               默认标签分为四种import标签,alias标签,bean标签,beans标签 四种类似,spring源码中分别对于这四种默认标签提供了对应的四个方法分别进行解析对应的标签。


  
  1. //对Spring beans xml 配置的默认标签进行解析 默认标签 包含几个大类
  2. // 1、import导入别的xml配置
  3. // 2、alias显示注册别名
  4. // 3、bean 对应配置的实例bean解析 是我们最常用同时也是最重要的解析方法(主要探究点)
  5. // 4、beans 即<beans /> 标签下包含一个内部的<beans /> (本地使用递归)
  6. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  7. //如果对应的doc节点是import 走import标签解析逻辑
  8. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  9. importBeanDefinitionResource(ele);
  10. }
  11. //如果对应的doc节点是alias 走注册别名的逻辑
  12. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  13. processAliasRegistration(ele);
  14. }
  15. //如果对应的doc节点是bean 走注册解析bean标签并转换为BeanDefinition对象的逻辑
  16. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  17. processBeanDefinition(ele, delegate);
  18. }
  19. //内部beans 标签解析 再次调用该类的doRegisterBeanDefinitions()
  20. //把其当做一个新的beans.xml直接进行解析
  21. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  22. // recurse 递归执行
  23. doRegisterBeanDefinitions(ele);
  24. }
  25. }

  1、import标签解析

           import标签可以使我们的spring配置结构清晰 对于比较有着庞大配置的项目,我们可以将具体的某个相似的功能bean配置 分模块的配置在单独的xml配置文件中,比如数据源相关的配置spring-datasource.xml、缓存相关的配置 spring-cache.xml ,在最终的配置xml文件中使用import的方式将其引入 条理清晰 可读性也好。


  
  1. //import标签解析的具体逻辑
  2. protected void importBeanDefinitionResource(Element ele) {
  3. //获取import标签的resource属性 该属性表示为一个新的资源文件路径
  4. String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
  5. if (!StringUtils.hasText(location)) {
  6. getReaderContext().error( "Resource location must not be empty", ele);
  7. return;
  8. }
  9. //对于resource属性如果有类似${user.dir}占位符的使用该方法调用
  10. //将占位符转换为真实的location值
  11. location = getReaderContext().getEnvironment().
  12. resolveRequiredPlaceholders(location);
  13. Set<Resource> actualResources = new LinkedHashSet<>( 4);
  14. // Discover whether the location is an absolute or relative URI
  15. boolean absoluteLocation = false;
  16. try {
  17. //如果location是URL或者绝对的uri
  18. //(意思是表明该资源绝对路径可以被识别能访问到真实的资源)
  19. absoluteLocation = ResourcePatternUtils.isUrl(location) ||
  20. //ResourceUtils.toURI(location).isAbsolute();
  21. }
  22. catch (URISyntaxException ex) {
  23. // cannot convert to an URI, considering the location relative
  24. // unless it is the well-known Spring prefix "classpath*:"
  25. }
  26. //资源是绝对路径
  27. if (absoluteLocation) {
  28. try {
  29. //最终还是调用我们前面追踪到的XmlBeanDefintion的loadBeanDefinitions()方法()
  30. int importCount = getReaderContext().getReader()
  31. .loadBeanDefinitions(location, actualResources);
  32. if (logger.isDebugEnabled()) {
  33. logger.debug( "Imported " + importCount + " bean definitions
  34. from URL location [" + location + "]");
  35. }
  36. }
  37. catch (BeanDefinitionStoreException ex) {
  38. getReaderContext().error(
  39. "Failed to import bean definitions from URL location
  40. [" + location + "]", ele, ex);
  41. }
  42. }
  43. else {
  44. // 没有绝对路径 则根据location获取相对资源 如果资源存在直接调用如上相同的方法
  45. // 资源不存在 获取其根路径+location(相对路径)得到一个相对路径资源同样调用如上相同的方法
  46. //其他情况 抛出异常信息
  47. try {
  48. int importCount;
  49. Resource relativeResource = getReaderContext().getResource().
  50. createRelative(location);
  51. if (relativeResource.exists()) {
  52. importCount = getReaderContext().getReader().
  53. loadBeanDefinitions(relativeResource);
  54. actualResources.add(relativeResource);
  55. }
  56. else {
  57. String baseLocation = getReaderContext().getResource().getURL().toString();
  58. importCount = getReaderContext().getReader().loadBeanDefinitions(
  59. StringUtils.applyRelativePath(baseLocation, location), actualResources);
  60. }
  61. if (logger.isDebugEnabled()) {
  62. logger.debug( "Imported " + importCount + " bean definitions from
  63. relative location [" + location + "]");
  64. }
  65. }
  66. catch (IOException ex) {
  67. getReaderContext().error( "Failed to resolve current resource location",
  68. ele, ex);
  69. }
  70. catch (BeanDefinitionStoreException ex) {
  71. getReaderContext().error( "Failed to import bean definitions from
  72. relative location [" + location + "]",ele, ex);
  73. }
  74. }
  75. Resource[] actResArray = actualResources.toArray( new Resource[ 0]);
  76. //触发事件监听器 通知import解析完成
  77. getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
  78. }

2、alias标签解析

       alias标签可以让我们显式为我们所定义的bean添加别名


  
  1. //别名标签注册 在进行bean定义的时候 除了使用id name定义名字外 还可以使用<alias />指定别名
  2. protected void processAliasRegistration(Element ele) {
  3. //获取alias标签的name属性 beanName
  4. //(该属性值和配置中的某个bean的id或者name有直接或者间接的联系
  5. //即beanName最终会指向配置中的某个bean)
  6. String name = ele.getAttribute(NAME_ATTRIBUTE);
  7. //获取alias标签的alias属性别名
  8. String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
  9. boolean valid = true;
  10. if (!StringUtils.hasText(name)) {
  11. getReaderContext().error( "Name must not be empty", ele);
  12. valid = false;
  13. }
  14. if (!StringUtils.hasText(alias)) {
  15. getReaderContext().error( "Alias must not be empty", ele);
  16. valid = false;
  17. }
  18. // name属性 和alias属性存在
  19. if (valid) {
  20. try {
  21. //获取注册别名的类进行别名注册 默认实现类SimpleAliasRegistry
  22. //使用map来存储name和alias的关联关系
  23. getReaderContext().getRegistry().registerAlias(name, alias);
  24. }
  25. catch (Exception ex) {
  26. getReaderContext().error( "Failed to register alias '" + alias +
  27. "' for bean with name '" + name + "'", ele, ex);
  28. }
  29. //触发事件监听器 通知alias解析完成
  30. getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
  31. }
  32. }

3、beans标签的解析

        beans.xml中嵌入式的beans标签,一般很少使用,但是非常类似于import标签的功能,本质上是将其当做beans标签递归调用解析beans的方法进行解析。此处不再进行描述。

4、bean标签的解析

        beans标签是我们最常用同时也是spring最核心的解析逻辑,spring用了大量的篇幅来解析相关的bean标签,接下来让我们一步一步分析一下bean标签的解析流程

   4.1、processBeanDefinition方法


  
  1. //最常见的最核心最终的bean标签的解析
  2. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  3. //解析bean标签 将其转换为BeanDefinition 保存在BeanDefinitionHolder中
  4. //(BeanDefinitionHolder中 持有一个BeanDefinition对象)
  5. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  6. if (bdHolder != null) {
  7. //当对应的BeanDefinitionHolder存在的时候 如果bean标签中有我们自定义的属性
  8. //或者自定义的子节点标签
  9. //对其自定义标签或者属性进行解析
  10. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  11. try {
  12. //注册最终解析并修饰后的BeanDefinition实例
  13. //经过上面两部的操作得到的BeanDefinition对象 已经满足后续的使用要求了
  14. // 接下来剩下的工作是注册对应的bean
  15. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
  16. getReaderContext().getRegistry());
  17. }
  18. catch (BeanDefinitionStoreException ex) {
  19. getReaderContext().error( "Failed to register bean definition with name '"
  20. bdHolder.getBeanName() + "'", ele, ex);
  21. }
  22. //发送注册bean的事件监听 通知该对应的bean定义已经注册成功
  23. getReaderContext().fireComponentRegistered(
  24. new BeanComponentDefinition(bdHolder));
  25. }
  26. }

  下面我们分析一下这个方法主要逻辑:

  • 委托BeanDefinitionParserDelegate 调用其parseBeanDefinitionElement()方法对bean的标签进行解析
  • BeanDefinitionHolder如果存在且其中我们自定义的属性 或者自定义的子节点标签 再次对自定义标签进行解析
  • 经过上面两部的操作得到的BeanDefinition对象 已经满足后续的使用要求了 接下来剩下的工作是注册BeanDefinition对象
  • 最后触发响应事件,通知相关的监视器告知其对应的beanDefinition对象加载完成

4.2、parseBeanDefinitionElement方法

           该方法主要是将bean的相关属性比如 id,class parent等进行解析填充到BeanDefintion实例并将其放BeanDefinitionHolder中。


  
  1. //解析Bean
  2. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
  3. //获取bean标签的id 和name属性 name属性根据",;"将其转换为数组存放在list中
  4. //如果没有id 则别名的一个座位其唯一标识beanName(也是bean的原始名字)
  5. String id = ele.getAttribute(ID_ATTRIBUTE);
  6. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  7. List<String> aliases = new ArrayList<>();
  8. if (StringUtils.hasLength(nameAttr)) {
  9. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  10. aliases.addAll(Arrays.asList(nameArr));
  11. }
  12. String beanName = id;
  13. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  14. beanName = aliases.remove( 0);
  15. if (logger.isDebugEnabled()) {
  16. logger.debug( "No XML 'id' specified - using '" + beanName +
  17. "' as bean name and " + aliases + " as aliases");
  18. }
  19. }
  20. if (containingBean == null) {
  21. //检查beanName是否已经被使用 没有被使用 将beanName和aliases 存放到usedNames列表中
  22. checkNameUniqueness(beanName, aliases, ele);
  23. }
  24. //前面只是针对id和name属性进行解析获取别名的处理 该行代码对bean标签的其他属性进行解析并初始化BeanDefinition
  25. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  26. if (beanDefinition != null) {
  27. //如果beanName不存在则使用spring中提供的命名规则生成对应的beanName
  28. if (!StringUtils.hasText(beanName)) {
  29. try {
  30. if (containingBean != null) {
  31. beanName = BeanDefinitionReaderUtils.generateBeanName(
  32. beanDefinition, this.readerContext.getRegistry(), true);
  33. }
  34. else {
  35. beanName = this.readerContext.generateBeanName(beanDefinition);
  36. //如果有可能的话为该beanDefinition再注册一个别名 别名为beanClassName
  37. String beanClassName = beanDefinition.getBeanClassName();
  38. if (beanClassName != null &&
  39. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  40. ! this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  41. aliases.add(beanClassName);
  42. }
  43. }
  44. if (logger.isDebugEnabled()) {
  45. logger.debug( "Neither XML 'id' nor 'name' specified - " +
  46. "using generated bean name [" + beanName + "]");
  47. }
  48. }
  49. catch (Exception ex) {
  50. error(ex.getMessage(), ele);
  51. return null;
  52. }
  53. }
  54. // 根据beanDefintion对象、beanName,别名列表 创建BeanDefinitionHolder 并返回
  55. String[] aliasesArray = StringUtils.toStringArray(aliases);
  56. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  57. }
  58. return null;
  59. }

  下面我们分析一下这个方法主要逻辑:

  • 解析bean的id,name属性转换为对应的beanName属性和别名alias列表
  • 调用parseBeanDefinitionElement()方法对除了已经解析的id,name属性其他属性进行解析并填充BeanDefinition对象
  • 如果beanName不存在则根据不同的规则生成对应的beanName
  • 将对应的beanDefintion属性、beanName、alias作为参数创建BeanDefintionHolder对象并返回。

4.3、parseBeanDefinitionElement()方法

       该方法对bean标签的除了id,name的其他属性进行解析,并将对应的属性值填充到BeanDefintion对象中。


  
  1. //对处理别名(bean标签的id属性和name属性)之外的其他标签进行解析处理
  2. @Nullable
  3. public AbstractBeanDefinition parseBeanDefinitionElement(
  4. Element ele, String beanName, @Nullable BeanDefinition containingBean) {
  5. //在parseState中存放一个beanName对应的BenEntry对象 表明该bean实例正在被解析为BeanDefinition
  6. //解析完成后该beanName对应的BenEntry对象应该会被移除
  7. this.parseState.push( new BeanEntry(beanName));
  8. //获取class属性
  9. String className = null;
  10. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  11. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  12. }
  13. //获取该实例需要继承的父类对象的bean(父类Bean的id 或者beanName)
  14. String parent = null;
  15. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  16. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  17. }
  18. try {
  19. //初始化一个AbstractBeanDefinition对象并设置属性beanClass 和parentName
  20. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  21. //设置Scope Abstract lazyInit、autowire、depends-on、autowire-candidate
  22. // primary、init-method、destroy-method、factory-method、factory-bean等属性
  23. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  24. //设置beanDefinition的描述信息
  25. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  26. //解析bean标签的子<meta />标签
  27. parseMetaElements(ele, bd);
  28. //解析bean标签的lookup-method子标签
  29. //将其转换为LookUpMethodOverride属性添加到BeanDefinition
  30. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  31. //解析bean标签的replaced-method子标签
  32. //将其转换为ReplaceOverride属性添加到BeanDefinition
  33. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  34. //解析bean标签的constructor-arg子标签
  35. parseConstructorArgElements(ele, bd);
  36. //解析bean标签的property子标签
  37. parsePropertyElements(ele, bd);
  38. //解析bean标签的Qualifier子标签
  39. parseQualifierElements(ele, bd);
  40. bd.setResource( this.readerContext.getResource());
  41. bd.setSource(extractSource(ele));
  42. return bd;
  43. }
  44. catch (ClassNotFoundException ex) {
  45. error( "Bean class [" + className + "] not found", ele, ex);
  46. }
  47. catch (NoClassDefFoundError err) {
  48. error( "Class that bean class [" + className + "] depends on not found", ele, err);
  49. }
  50. catch (Throwable ex) {
  51. error( "Unexpected failure during bean definition parsing", ele, ex);
  52. }
  53. finally {
  54. //移除解析标记
  55. this.parseState.pop();
  56. }
  57. return null;
  58. }

 

至此通过xml配置一个普通的bean 并交由spring管理 然后XmlBeanFactory 获取的第一部分(处理具体的解析document初始阿化BeanDefinition对象)总结完成。笔者才疏学浅,在本篇博文的分析中如果有理解不到的地方,欢迎大家批评指正。


转载:https://blog.csdn.net/liushangzaibeijing/article/details/105173989
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场