Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

一. 前言

Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别。本篇就以BeanFactory基础容器接口的默认实现类XmlBeanFactory启动流程分析来入门Spring源码的学习。


二. 概念要点

1. 概念定义

BeanDefinition:Bean元数据描述,Bean在Spring IOC容器中的抽象,是Spring的一个核心概念

DefaultListableBeanFactory : Spring IOC容器的实现,可以作为一个独立使用的容器, Spring IOC容器的始祖

XmlBeanFactory:继承自DefaultListableBeanFactory,与其不同点在于XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取

ApplicationContext: 高级容器定义接口,基于BeanFactory添加了扩展功能,如ResourceLoader、MessageSource、ApplicationEventPublisher等

2. 糟糕!XmlBeanFactory被废弃了

对Spring有些了解的应该XmlBeanFactory已经过时了。没错,本篇要讲的XmlBeanFactory在Spring3.1这个很久远版本就开始过时了。


@deprecated as of Spring 3.1 in favor of {@link DefaultListableBeanFactory}

1



取而代之的写法如下


ClassPathResource resource=new ClassPathResource("spring-config.xml");

DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();

XmlBeanDefinitionReader beanDefinitionReader=new XmlBeanDefinitionReader(beanFactory);

beanDefinitionReader.loadBeanDefinitions(resource);

1

2

3

4

概括DefaultListableBeanFactory + XmlBeanDefinitionReader 取代了 XmlBeanFactory容器的创建和初始化,可以联想到通过组合的方式灵活度是比XmlBeanFactory高的,针对不同的资源读取组合的方式只需替换不同的读取器BeanDefinitionReader就可以了,而XmlBeanFactory中的读取器XmlBeanDefinitionReader限定其只能读取XML格式的文件资源,所以至于XmlBeanFactory被废弃的原因可想而知。


3. XmlBeanFactory?!XML,你会XML解析吗?

0

调用成功


1

2

3

4

附上DOM4J解析的代码


String xml="\n" +

               "0\n" +

               "调用成功\n" +

               "";

Document document = DocumentHelper.parseText(xml);

Element root = document.getRootElement();

String code = root.elementText("code");

String message =root.elementText("message");

1

2

3

4

5

6

7

8

微信图片_20230709211913.png


XML文档被解析成DOM树,其中Document是整个DOM的根节点,root为根元素,由根元素一层一层向下解析element元素,容器启动解析XML流程就是这样。


三. XmlBeanFactory启动流程分析

XmlBeanFactory容器启动就两行代码


ClassPathResource resource = new ClassPathResource("spring-config.xml");

XmlBeanFactory beanFactory = new XmlBeanFactory(resource);

1

2

怎么样?看似简单就证明了Java封装的强大,但背后藏了太多。 这里就送上XmlBeanFactory启动流程图,对应的就是上面的两行代码。

微信图片_20230709211933.png



这是啥?!看得头晕的看官老爷们别急着给差评啊(瑟瑟发抖中)。这里精简下,想快速理解的话直接看精简版后的,想深入到细节建议看下上面的时序图。

微信图片_20230709211944.png

整个就是bean的加载阶段。通过解析XML中的标签元素生成beanDefinition注册到beanDefinitionMap中。


四. XmlBeanFactory启动源码解析

按照XmlBeanFactory启动流程的先后顺序整理的关键性代码索引列表,其中一级索引为类,二级索引对应其类下的方法。符号 —▷表示接口的实现。建议可以观察方法索引的参数变化(资源转换)来分析整个流程,来加深对流程的理解。


XmlBeanFactory


XmlBeanFactory(Resource resource)

XmlBeanDefinitionReader


loadBeanDefinitions(Resource resource)

doLoadBeanDefinitions(InputSource inputSource, Resource resource)

registerBeanDefinitions(Document doc, Resource resource)

DefaultBeanDefinitionDocumentReader —▷ BeanDefinitionDocumentReader


registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

doRegisterBeanDefinitions(Element root)

parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)

parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)

processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)

BeanDefinitionParserDelegate


parseBeanDefinitionElement(Element ele)

decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder originalDef,…)

DefaultListableBeanFactory —▷ BeanDefinitionRegistry


registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

好了,当你心里对这个流程有个大概的样子之后,这里就可以继续走下去。建议还是很模糊的同学自重,避免走火入魔想对我人身攻击的。下面就每个方法重要的点一一解析,严格按照方法的执行先后顺序来说明。


1.1 XmlBeanFactory(Resource resource)

功能概述: XmlBeanFactory的构造方法,整个容器启动的入口,完成bean工厂的实例化和BeanDefinition加载(解析和注册)。


/**

* XmlBeanFactory

**/

public XmlBeanFactory(Resource resource) throws BeansException {

   this(resource, null);

}


public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException  {

super(parentBeanFactory);

// 加载(解析注册)BeanDefinition

this.reader.loadBeanDefinitions(resource);

}


知识点:


Resource:Resource接口是Spring资源访问策略的抽象,,而具体的资源访问方式由其实现类完成,如类路径资源(ClassPathResource)、文件(FileSystemResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource),根据不同的类型的资源使用对应的访问策略,明明白白的策略模式。

2.1 loadBeanDefinitions(Resource resource)

功能概述: 上面根据ClassPathResource资源访问策略拿到了资源Resource,这里将Resource进行特定的编码处理,然后将编码后的Resource转换成SAX解析XML文件所需要的输入源InputSource。


/**

* XmlBeanDefinitionReader

**/

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {

// 把从XML文件读取的Resource资源进行编码处理

   return loadBeanDefinitions(new EncodedResource(resource));

}


/**

* XmlBeanDefinitionReader

**/

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {

   ...

   // 完成resource->inputStream->inputSource转换,使用SAX方式接收输入源InputSource解析XML

   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {

       InputSource inputSource = new InputSource(inputStream);

       if (encodedResource.getEncoding() != null) {

           inputSource.setEncoding(encodedResource.getEncoding());

       }

       // 执行加载BeanDefinition

       return doLoadBeanDefinitions(inputSource, encodedResource.getResource());

   }

   ...

}


知识点:


XML解析两种方式:SAX(Simple API for XML)和DOM(Document Object Model),区别在于SAX按照顺序逐行读取,查找到符合条件即停止,只能读不能修改,适合解析大型XML;DOM则是一次性读取到内存建立树状结构,占用内存,不仅能读还能修改XML。Spring采用的SAX方式来解析XML。

2.2 doLoadBeanDefinitions(InputSource inputSource, Resource resource)

功能概述: 获取DOM Document对象,XmlBeanDefinitionReader本身没有对文档读取的能力,而是委托给DocumentLoader的实现类DefaultDocumentLoader去读取输入源InputResource从而得到Document对象。获得Document对象后,接下来就是BeanDefinition的解析和注册。


/**

* XmlBeanDefinitionReader

**/

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

   throws BeanDefinitionStoreException {

   ...

       try {

           // inputSource->DOM Document

           Document doc = doLoadDocument(inputSource, resource);

           int count = registerBeanDefinitions(doc, resource);

           return count;

       }

   ...

}


知识点:


Document是DOM的根节点,提供对文档数据访问的入口。

2.3 registerBeanDefinitions(Document doc, Resource resource)

功能概述: 解析DOM Document成容器的内部数据接口BeanDefinition并注册到容器内部。


/**

* XmlBeanDefinitionReader

**/

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

// 已注册的BeanDefinition的数量

   int countBefore = getRegistry().getBeanDefinitionCount();

// DOM Document->BeanDefinition,注册BeanDefinition至容器

   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

   // 返回此次注册新增的BeanDefinition数量

return getRegistry().getBeanDefinitionCount() - countBefore;

}


知识点:


BeanDefinitionDocumentReader是接口,实际完成对DOM Document解析的是其默认实现类DefaultBeanDefinitionDoucumentReade。

createReaderContext(resource)创建XmlReaderContext上下文,包含了XmlBeanDefinitionReader读取器和NamespaceHandlerResolver 命名空间解析器。

3.1 registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

功能概述: 在此之前一直 是XML加载解析的准备阶段,在获取到Document对象之后就开始真正的解析了。


/**

* DefaultBeanDefinitionDocumentReader

**/

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

this.readerContext = readerContext;

doRegisterBeanDefinitions(doc.getDocumentElement());

}

1

2

3

4

5

6

7

3.2 doRegisterBeanDefinitions(Element root)

功能概述: 解析Element,DefaultBeanDefinitionDoucumentReader同样不具有解析Element的能力,委托给BeanDefinitionParserDelegate执行。


/**

* DefaultBeanDefinitionDocumentReader

**/

protected void doRegisterBeanDefinitions(Element root) {

   BeanDefinitionParserDelegate parent = this.delegate;

   this.delegate = createDelegate(getReaderContext(), root, parent);

   ...

   // 解析前处理,留给子类实现

preProcessXml(root);

parseBeanDefinitions(root, this.delegate);

   // 解析后处理,留给子类实现

postProcessXml(root);


this.delegate = parent;

}

知识点:


preProcessXml和postProcessXml里代码是空的,这两个方法是面向子类设计的,设计模式中的模板方法,也可以说是Spring的一个扩展点,后面有机会可以深入下细节。

3.3 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)

功能概述: 判别XML中Bean的声明标签是默认的还是自定义的,执行不同的解析逻辑。对于根节点或者子节点是默认命名空间采用parseDefaultElement,否则使用parseCustomElement对自定义命名空间解析。


/**

* DefaultBeanDefinitionDocumentReader

**/

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){

// 对beans的处理

   if (delegate.isDefaultNamespace(root)) {

 NodeList nl = root.getChildNodes();

 for (int i = 0; i < nl.getLength(); i++) {

  Node node = nl.item(i);

  if (node instanceof Element) {

   Element ele = (Element) node;

   if (delegate.isDefaultNamespace(ele)) {

                   // 对默认的Bean标签解析

    parseDefaultElement(ele, delegate);

   }

   else {

                   // 对自定义的Bean标签解析

    delegate.parseCustomElement(ele);

   }

  }

 }

}

else {

 delegate.parseCustomElement(root);

}

}


知识点:


Spring中XML有两大类Bean的声明标签


默认声明标签:


1

自定义声明标签: ·


1

怎么区分是默认命名空间还是自定义命名空间?


通过node.getNamespaceURI()获取命名空间并和Spring中固定的命名空间http://www.springframework.org/schema/beans进行比对,如果一致则默认,否则自定义。


3.4 parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)

功能概述: 上面说到Spring标签包括默认标签和自定义标签两种。解析这两种方式分别不同。以下就默认标签进行说明BeanDefinitionParseDelegate对Bean标签元素的解析过程。


/**

* DefaultBeanDefinitionDocumentReader

**/

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {

   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {

       // import标签

 importBeanDefinitionResource(ele);

}

else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {

       // alias标签

 processAliasRegistration(ele);

}

else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {

       // bean标签

 processBeanDefinition(ele, delegate);

}

else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {

 // beans标签

 doRegisterBeanDefinitions(ele);

}

}


知识点:


Spring的4种默认标签举例:


import


1

alias


1

2

bean



1

2

3

beans



1

2

3

3.5 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)

功能概述: 在4中默认标签当中,其中核心的是对bean标签的解析。以下就对bean标签的解析来看解析过程。


/**

* DefaultBeanDefinitionDocumentReader

**/

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate){

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // 参考4.1源码

if (bdHolder != null) {

 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 参考4.2源码

 try {

  BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 参考5.1源码

 }

 ...

 // Send registration event.

 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

}

}



1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

流程解读:


DefaultBeanDefinitionDocumentReader委托BeanDefinitionParseDelegate的parseBeanDefinitionElement方法进行标签元素的解析。解析后返回BeanDefinitionHolder实例bdHolder,bdHolder实例包含了配置文件中的id、name、alias之类的属性。

返回的bdHolder不为空时,标签元素如果有自定义属性和自定义子节点,还需要再次对以上两个标签解析。具体逻辑参考4.2小节源码。

解析完成后,对bdHolder进行注册,使用BeanDefinitionReaderUtils.registerBeanDefinition()方法。具体逻辑参考5.1小节源码。

发出响应事件,通知相关监听器,bean已经解析完成。

4.1 registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

功能概述: 通过BeanDefinitionParseDelegate对Bean标签的解析,解析得到id和name这些信息封装到BeanDefinitionHolder并返回。


/**

* BeanDefinitionParseDelegate

**/

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {

   return parseBeanDefinitionElement(ele, null);

}

/**

* BeanDefinitionParseDelegate

**/

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {

       // 解析id属性

 String id = ele.getAttribute(ID_ATTRIBUTE);

       // 解析name属性

 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);


 List aliases = new ArrayList<>();

 if (StringUtils.hasLength(nameAttr)) {

  String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);

  aliases.addAll(Arrays.asList(nameArr));

 }


 String beanName = id;

 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {

  beanName = aliases.remove(0);

 }


 if (containingBean == null) {

  checkNameUniqueness(beanName, aliases, ele);

 }


 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

   if (beanDefinition != null) {

  ...

  String[] aliasesArray = StringUtils.toStringArray(aliases);

  return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);

 }

}



4.2 decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd)

功能概述: 4.1是对默认标签的属性解析,那如果标签有自定义属性和自定义子节点,这时就要通过decorateBeanDefinitionIfRequired解析这些自定义属性和自定义子节点。


/**

* BeanDefinitionParseDelegate

**/

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(

   Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {


   BeanDefinitionHolder finalDefinition = originalDef;


   // Decorate based on custom attributes first.

   // 首先对自定义属性解析和装饰

   NamedNodeMap attributes = ele.getAttributes();

   for (int i = 0; i < attributes.getLength(); i++) {

       Node node = attributes.item(i);

       finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);

   }


   // Decorate based on custom nested elements.

   // 装饰标签下的自定义子节点

   NodeList children = ele.getChildNodes();

   for (int i = 0; i < children.getLength(); i++) {

       Node node = children.item(i);

       if (node.getNodeType() == Node.ELEMENT_NODE) {

           finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);

       }

   }

   return finalDefinition;

}


5.1 registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

功能概述: 通过beanName注册BeanDefinition至BeanDefinitionMap中去,至此完成Bean标签的解析,转换成Bean在容器中的数据结构BeanDefinition.


/**

* BeanDefinitionReaderUtils

**/

public static void registerBeanDefinition(

  BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)

 throws BeanDefinitionStoreException {


// Register bean definition under primary name.

   // 使用beanName注册BeanDefinition

String beanName = definitionHolder.getBeanName();

 

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());


// Register aliases for bean name, if any.

   // 使用别名注册BeanDefiniion

String[] aliases = definitionHolder.getAliases();

if (aliases != null) {

 for (String alias : aliases) {

  registry.registerAlias(beanName, alias);

 }

}

}


/**

* BeanDefinitionParseDelegate

**/

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

  throws BeanDefinitionStoreException {

   ...

   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);

if (existingDefinition != null) {

    ...

    this.beanDefinitionMap.put(beanName, beanDefinition);

}else{

    if (hasBeanCreationStarted()) {

  // beanDefinitionMap是全局变量,会存在并发访问问题

  synchronized (this.beanDefinitionMap) {

   this.beanDefinitionMap.put(beanName, beanDefinition);

   List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);

   updatedDefinitions.addAll(this.beanDefinitionNames);

   updatedDefinitions.add(beanName);

   this.beanDefinitionNames = updatedDefinitions;

   removeManualSingletonName(beanName);

  }

 }

 else {

  // Still in startup registration phase

  this.beanDefinitionMap.put(beanName, beanDefinition);

  this.beanDefinitionNames.add(beanName);

  removeManualSingletonName(beanName);

 }

 this.frozenBeanDefinitionNames = null;

}

   ...

}


五. 结语

总结Spring IOC基础容器XmlBeanFactory的启动流程概括如下:


执行XmlFactoryBean构造方法,执行加载BeanDefinition方法。

使用XmlBeanDefinitionReader读取器将资源Resource解析成DOM Document对象。

使用DefaultBeanDefinitionDocumentReader读取器从Document对象解析出 Element。

通过BeanDefinitionParserDelegate将Element转换成对应的BeanDefinition。

实现BeanDefinitionRegistry注册器将解析好的BeanDefinition注册到容器中的BeanDefitionMap里去

本篇就XmlBeanFactory容器启动流程分析和源码解析两个角度来对Spring IOC容器有个基础的认识。有了这篇基础,下篇就开始对Spring的扩展容器ApplicationContext进行分析。最后希望大家看完都有所收获,可以的话给个关注,感谢啦。


六. 附录

附上我编译好的Spring源码,版本是当前最新版本5.3.0,欢迎star


spring-framework-5.3.0编译源码



相关文章
|
3月前
|
监控 安全 开发工具
鸿蒙HarmonyOS应用开发 | HarmonyOS Next-从应用开发到上架全流程解析
HarmonyOS Next是华为推出的最新版本鸿蒙操作系统,强调多设备协同和分布式技术,提供丰富的开发工具和API接口。本文详细解析了从应用开发到上架的全流程,包括环境搭建、应用设计与开发、多设备适配、测试调试、应用上架及推广等环节,并介绍了鸿蒙原生应用开发者激励计划,帮助开发者更好地融入鸿蒙生态。通过DevEco Studio集成开发环境和华为提供的多种支持工具,开发者可以轻松创建并发布高质量的鸿蒙应用,享受技术和市场推广的双重支持。
430 11
|
4月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
144 2
|
26天前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
91 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
17天前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
63 12
|
12天前
|
域名解析 弹性计算 负载均衡
新手上云教程参考:阿里云服务器租用、域名注册、备案及域名解析流程图文教程
对于想要在阿里云上搭建网站或应用的用户来说,购买阿里云服务器和注册域名,绑定以及备案的流程至关重要。本文将以图文形式为您介绍阿里云服务器购买、域名注册、备案及绑定的全流程,以供参考,帮助用户轻松上手。
|
4天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
112 7
|
3月前
|
域名解析 弹性计算 安全
阿里云服务器租用、注册域名、备案及域名解析完整流程参考(图文教程)
对于很多初次建站的用户来说,选购云服务器和注册应及备案和域名解析步骤必须了解的,目前轻量云服务器2核2G68元一年,2核4G4M服务器298元一年,域名注册方面,阿里云推出域名1元购买活动,新用户注册com和cn域名2年首年仅需0元,xyz和top等域名首年仅需1元。对于建站的用户来说,购买完云服务器并注册好域名之后,下一步还需要操作备案和域名绑定。本文为大家展示阿里云服务器的购买流程,域名注册、绑定以及备案的完整流程,全文以图文教程形式为大家展示具体细节及注意事项,以供新手用户参考。
|
4月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
90 12

热门文章

最新文章

推荐镜像

更多