spring源码--容器的基本实现

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:




spring源码各版本下载地址:

    https://github.com/spring-projects/spring-framework/tags


在工作中见得非常多的容器使用是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<beans xmlns= "http://www.springframework.org/schema/beans"
     xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"  xmlns:util= "http://www.springframework.org/schema/util"
     xmlns:jee= "http://www.springframework.org/schema/jee"  xmlns:aop= "http://www.springframework.org/schema/aop"
     xmlns:tx= "http://www.springframework.org/schema/tx"  xmlns:context= "http://www.springframework.org/schema/context"
     xmlns:p= "http://www.springframework.org/schema/p"  xmlns:cache= "http://www.springframework.org/schema/cache"
     xmlns:task= "http://www.springframework.org/schema/task"
     xsi:schemaLocation="
     http: //www.springframework.org/schema/task 
     http: //www.springframework.org/schema/task/spring-task-4.1.xsd
     http: //www.springframework.org/schema/beans
     http: //www.springframework.org/schema/beans/spring-beans-4.1.xsd
     http: //www.springframework.org/schema/aop 
     http: //www.springframework.org/schema/aop/spring-aop-4.1.xsd
     http: //www.springframework.org/schema/tx
     http: //www.springframework.org/schema/tx/spring-tx-4.1.xsd
     http: //www.springframework.org/schema/context
     http: //www.springframework.org/schema/context/spring-context-4.1.xsd
     http: //www.springframework.org/schema/util 
     http: //www.springframework.org/schema/util/spring-util-4.1.xsd
     http: //www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd">
     <bean id= "adapConsumer"  class = "com.ad.jms.AdapConsumer" >
           <property name= "queueChannel"  ref= "inputFromKafkaAdap" ></property>
     </bean>
</beans>
1
2
final  ApplicationContext app =  new  ClassPathXmlApplicationContext( "applicationContext.xml" );
AdapConsumer adConsumer = app.getBean( "adapConsumer" , AdapConsumer. class );

其底层实现原理是怎样的?根据《Spring源码深度解析》以另外一个容器BeanFactory来解析源码实现。

image.png



一、可以发现BeanFactory是通过XmlBeanFactory创建的,而XmlBeanFactory继承了DefaultListableBeanFactory,主要增加了XMLBeanDefinitionReader,接下来先认识一下这两个类。

    1.1 DefaultListableBeanFactory

image.png

其中各个类的作用

image.png

image.png

image.png

    

    1.2 XMLBeanDefinitionReader

完成了对配置文件进行封装后配置文件的读取工作。

image.png


二、我们根据BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))来分析建立过程

    2.1 new ClassPathResource("beanFactoryTest.xml")

这是通过ClassPathResource构造函数来构造一个ReSource,然后交给XmlBeanFactory的构造函数。那么ClassPathResource怎么处理的?


ReSource底层实现:

image.png

image.png

    

    2.2 new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  class  XmlBeanFactory  extends  DefaultListableBeanFactory {
 
    private  final  XmlBeanDefinitionReader reader =  new  XmlBeanDefinitionReader( this );
 
    public  XmlBeanFactory(Resource resource)  throws  BeansException {
       this (resource,  null );
    }
 
    public  XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)  throws  BeansException {
       super (parentBeanFactory);
       this .reader.loadBeanDefinitions(resource);
       
    }
}
//最后一句代码主要完成下面三个功能

image.png

image.png

可以看到有XMLBeanDefinitionReader对象(完成了对配置文件进行封装后配置文件的读取工作),就是加载bean,在代码中一层一层往下看:

1
2
3
public  int  loadBeanDefinitions(Resource resource)  throws  BeanDefinitionStoreException {
    return  loadBeanDefinitions( new  EncodedResource(resource));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public  int  loadBeanDefinitions(EncodedResource encodedResource)  throws  BeanDefinitionStoreException {
    Assert.notNull(encodedResource,  "EncodedResource must not be null" );
    if  (logger.isInfoEnabled()) {
       logger.info( "Loading XML bean definitions from "  + encodedResource.getResource());
    }
 
    Set<EncodedResource> currentResources =  this .resourcesCurrentlyBeingLoaded.get();
    if  (currentResources ==  null ) {
       currentResources =  new  HashSet<>( 4 );
       this .resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if  (!currentResources.add(encodedResource)) {
       throw  new  BeanDefinitionStoreException(
             "Detected cyclic loading of "  + encodedResource +  " - check your import definitions!" );
    }
    try  {
       InputStream inputStream = encodedResource.getResource().getInputStream();
       try  {
          InputSource inputSource =  new  InputSource(inputStream);
          if  (encodedResource.getEncoding() !=  null ) {
             inputSource.setEncoding(encodedResource.getEncoding());
          }
          return  doLoadBeanDefinitions(inputSource, encodedResource.getResource());
       }
       finally  {
          inputStream.close();
       }
    }
    catch  (IOException ex) {
       throw  new  BeanDefinitionStoreException(
             "IOException parsing XML document from "  + encodedResource.getResource(), ex);
    }
    finally  {
       currentResources.remove(encodedResource);
       if  (currentResources.isEmpty()) {
          this .resourcesCurrentlyBeingLoaded.remove();
       }
    }
}

上面这段代码真正进入了数据的准备阶段执行下面正式进入核心部分:

1
doLoadBeanDefinitions(inputSource, encodedResource.getResource())

doLoadBeanDefinitions之所以很核心,因为做了三件重要的事情:

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protected  int  doLoadBeanDefinitions(InputSource inputSource, Resource resource)
       throws  BeanDefinitionStoreException {
    try  {
       Document doc = doLoadDocument(inputSource, resource);
       return  registerBeanDefinitions(doc, resource);
    }
    catch  (BeanDefinitionStoreException ex) {
       throw  ex;
    }
    catch  (SAXParseException ex) {
       throw  new  XmlBeanDefinitionStoreException(resource.getDescription(),
             "Line "  + ex.getLineNumber() +  " in XML document from "  + resource +  " is invalid" , ex);
    }
    catch  (SAXException ex) {
       throw  new  XmlBeanDefinitionStoreException(resource.getDescription(),
             "XML document from "  + resource +  " is invalid" , ex);
    }
    catch  (ParserConfigurationException ex) {
       throw  new  BeanDefinitionStoreException(resource.getDescription(),
             "Parser configuration exception parsing XML from "  + resource, ex);
    }
    catch  (IOException ex) {
       throw  new  BeanDefinitionStoreException(resource.getDescription(),
             "IOException parsing XML document from "  + resource, ex);
    }
    catch  (Throwable ex) {
       throw  new  BeanDefinitionStoreException(resource.getDescription(),
             "Unexpected exception parsing XML document from "  + resource, ex);
    }
}
protected  Document doLoadDocument(InputSource inputSource, Resource resource)  throws  Exception {
    return  this .documentLoader.loadDocument(inputSource, getEntityResolver(),  this .errorHandler,
          getValidationModeForResource(resource), isNamespaceAware());
}
public  int  registerBeanDefinitions(Document doc, Resource resource)  throws  BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int  countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return  getRegistry().getBeanDefinitionCount() - countBefore;
}

接下来将这三件事情逐个分析:

    2.2.1 获取xml文件的验证模式

    DTD和XSD的区别:

image.png

image.png

 

    验证模式的读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected  int  getValidationModeForResource(Resource resource) {
    int  validationModeToUse = getValidationMode();
    if  (validationModeToUse != VALIDATION_AUTO) {
       return  validationModeToUse;
    }
    int  detectedMode = detectValidationMode(resource);
    if  (detectedMode != VALIDATION_AUTO) {
       return  detectedMode;
    }
    // Hmm, we didn't get a clear indication... Let's assume XSD,
    // since apparently no DTD declaration has been found up until
    // detection stopped (before finding the document's root tag).
    return  VALIDATION_XSD;
}

    2.2.2 读取document

image.png

1
2
3
4
protected  Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return  this .documentLoader.loadDocument(inputSource, getEntityResolver(),  this .errorHandler,
          getValidationModeForResource(resource), isNamespaceAware());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package  org.springframework.beans.factory.xml;
 
import  org.w3c.dom.Document;
import  org.xml.sax.EntityResolver;
import  org.xml.sax.ErrorHandler;
import  org.xml.sax.InputSource;
 
public  interface  DocumentLoader {
 
    Document loadDocument(
          InputSource inputSource, EntityResolver entityResolver,
          ErrorHandler errorHandler,  int  validationMode,  boolean  namespaceAware)
          throws  Exception;
 
}

    2.2.3 根据返回的document注册bean信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected  int  doLoadBeanDefinitions(InputSource inputSource, Resource resource)
       throws  BeanDefinitionStoreException {
    try  {
       Document doc = doLoadDocument(inputSource, resource);
       return  registerBeanDefinitions(doc, resource);
    }
    catch  (BeanDefinitionStoreException ex) {
       throw  ex;
    }
    catch  (SAXParseException ex) {
       throw  new  XmlBeanDefinitionStoreException(resource.getDescription(),
             "Line "  + ex.getLineNumber() +  " in XML document from "  + resource +  " is invalid" , ex);
    }
    catch  (SAXException ex) {
       throw  new  XmlBeanDefinitionStoreException(resource.getDescription(),
             "XML document from "  + resource +  " is invalid" , ex);
    }
    catch  (ParserConfigurationException ex) {
       throw  new  BeanDefinitionStoreException(resource.getDescription(),
             "Parser configuration exception parsing XML from "  + resource, ex);
    }
    catch  (IOException ex) {
       throw  new  BeanDefinitionStoreException(resource.getDescription(),
             "IOException parsing XML document from "  + resource, ex);
    }
    catch  (Throwable ex) {
       throw  new  BeanDefinitionStoreException(resource.getDescription(),
             "Unexpected exception parsing XML document from "  + resource, ex);
    }
}

registerBeanDefinitions方法:

1
2
3
4
5
6
public  int  registerBeanDefinitions(Document doc, Resource resource)  throws  BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int  countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return  getRegistry().getBeanDefinitionCount() - countBefore;
}

image.png

image.png

image.png

image.png

image.png

解析并注册BeanDefinition

image.png


image.png

image.png



本文转自 叫我北北 51CTO博客,原文链接:http://blog.51cto.com/qinbin/2059124


相关文章
|
3月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
130 2
|
3月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
152 5
|
1月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
2月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
73 6
|
2月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
74 2
|
2月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
50 1
|
3月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
94 9
|
3月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
3月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
70 0
|
4月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。