简介

dom4j 采用 DOM 方式解析 xml,可以读写 xml 文件,并且支持 Xpath 来获取节点。目前,由于其出色的性能和易用性,目前 dom4j 已经得到广泛使用,例如 Spring、 Hibernate 就是使用 dom4j 来解析xml配置。

注意,dom4j 使用 Xpath需要额外引入 jaxen 的包,另外,针对不需要随机访问节点的读场景,建议采用基于事件驱动的 SAX。

本文将包含以下内容(篇幅较长,可根据需要选择阅读):

  1. 认识 DOM、SAX、JAXP 和 DOM4j;
  2. 使用例子
  3. 源码分析

认识DOM、SAX、JAXP和DOM4J

其实,JDK已经带有可以解析xml的api,如 DOM、SAX、JAXP,但为什么 dom4j 会更受欢迎呢?它们有什么区别呢?在学习 dom4j 之前,需要先理解下 DOM、SAX 等概念,因为 dom4j 就是在此基础上改进而来。

xerces解释器

先介绍下 xerces 解释器,下面介绍的 SAX、DOM 和 JAXP 都只是接口,而 xerces 解释器就是它们的具体实现,在com.sun.org.apache.xerces.internal包。xerces 被称为性能最好的解释器,除了 xerces 外,还有其他的第三方解释器,如 crimson。

SAX--事件驱动

JDK针对解析xml提供的接口,不是具体实现,在org.xml.sax包。SAX 是基于事件处理,解析过程中根据当前的XML元素类型,调用用户自己实现的回调方法,如:startDocument()startElement()

下面以例子说明,通过 SAX 解析xml并打印节点名:

    /*这里解释下四个的接口:
EntityResolver:需要实现resolveEntity方法。当解析xml需要引入外部数据源时触发,通过这个方法可以重定向到本地数据源或进行其他操作。
DTDHandler:需要实现notationDecl和unparsedEntityDecl方法。当解析到"NOTATION", "ENTITY"或 "ENTITIES"时触发。
ContentHandler:最常用的一个接口,需要实现startDocument、endDocument、startElement、endElement等方法。当解析到指定元素类型时触发。
ErrorHandler:需要实现warning、error或fatalError方法。当解析出现异常时会触发。
*/
@Test
public void test04() throws Exception {
//DefaultHandler实现了EntityResolver, DTDHandler, ContentHandler, ErrorHandler四个接口
DefaultHandler handler = new DefaultHandler() {
@Override
//当解析到Element时,触发打印该节点名
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println(qName);
}
};
//获取解析器实例
XMLReader xr = XMLReaderFactory.createXMLReader();
//设置处理类
xr.setContentHandler(handler);
/*
* xr.setErrorHandler(handler);
* xr.setDTDHandler(handler);
* xr.setEntityResolver(handler);
*/
xr.parse(new InputSource("members.xml"));
}

因为 SAX 是基于事件处理的,不需要等到整个xml文件都解析完才执行我们的操作,所以效率较高。但 SAX 存在一个较大缺点,就是不能随机访问节点,因为 SAX 不会主动地去保存处理过的元素(优点就是内存占用小、效率高),如果想要保存读取的元素,开发人员先构建出一个xml树形结构,再手动往里面放入元素,非常麻烦(本质上 dom4j 就是通过 SAX 来构建xml树)。

DOM--文档对象模型

JDK针对解析xml提供的接口,不是具体实现,在org.w3c.dom包。DOM 采用了解析方式是一次性加载整个XML文档,在内存中形成一个树形的数据结构,开发人员可以随机地操作元素。见以下例子:

    @SuppressWarnings("restriction")
@Test
public void test05() throws Exception {
//获得DOMParser对象
com.sun.org.apache.xerces.internal.parsers.DOMParser domParser = new com.sun.org.apache.xerces.internal.parsers.DOMParser();
//解析文件
domParser.parse(new InputSource("members.xml"));
//获得Document对象
Document document=domParser.getDocument();
// 遍历节点
printNodeList(document.getChildNodes());
}

通过DOM解析,我们可以获取任意节点进行操作。但是,DOM 有两个缺点:

  1. 由于一次性加载整个XML文件到内存,当处理较大文件时,容易出现内存溢出。
  2. 节点的操作还是比较繁琐。

JAXP

封装了 SAX、DOM 两种接口,它并没有为JAVA解析XML提供任何新功能,只是对外提供更解耦、简便操作的API。如下:

DOM解析器

    @Test
public void test02() throws Exception {
// 获得DocumentBuilder对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析xml文件,获得Document对象
Document document = builder.parse("members.xml");
// 遍历节点
printNodeList(document.getChildNodes());
}

获取SAX解析器

    @Test
public void test03() throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
saxParser.parse("members.xml", new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println(qName);
}
});
}

其实,JAXP 并没有很大程度提高 DOM 和 SAX 的易用性,更多地体现在获取解析器时实现解耦,并没有解决 SAX 和 DOM 的缺点。

DOM4j

dom4j 本质上采用的是 DOM 方式解析 xml,也就是说它支持随机访问节点,相比 JDK 的 DOM 和 JAXP,dom4j 提供了更加简便的api(但是,考虑可移植,许多项目还是会采用 JAXP)。 以下通过使用例子和源码分析将作出说明。

项目环境

工程环境

JDK:1.8

maven:3.6.1

IDE:sts4

dom4j:2.1.1

创建项目

项目类型Maven Project,打包方式jar。

引入依赖

注意:dom4j 使用 XPath,必须引入 jaxen 的 jar 包。

<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- dom4j的jar包 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<!-- dom4j使用XPath需要的jar包 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<!-- 配置BeanUtils的包,这个我自定义工具类用的,如果只是简单使用dom4j可以不引入 -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>

使用例子--生成xml文件

本例子将分别使用 dom4j 和 JDK 的 DOM 接口生成 xml 文件(使用JDK的 DOM 接口时会使用 JAXP 的 API)。

需求

构建xml树,添加节点,并生成xml文件。格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<members>
<students>
<student name="张三" location="河南" age="18"/>
<student name="李四" location="新疆" age="26"/>
<student name="王五" location="北京" age="20"/>
</students>
<teachers>
<teacher name="zzs" location="河南" age="18"/>
<teacher name="zzf" location="新疆" age="26"/>
<teacher name="lt" location="北京" age="20"/>
</teachers>
</members>

生成xml文件--使用w3c的DOM接口

主要步骤

  1. 通过 JAXP 的API获得 Document 对象,这个对象可以看成xml的树;

  2. 将对象转化为节点,并添加在 Document 这棵树上;

  3. 通过 Transformer 对象将树输出到文件中。

编写测试类

路径:test目录下的cn.zzs.dom4j

注意:因为使用的是w3c的 DOM 接口,所以节点对象导的是org.w3c.dom包,而不是org.dom4j包。

    @Test
public void test02() throws Exception {
// 创建工厂对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 创建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 创建Document对象
Document document = documentBuilder.newDocument(); // 创建根节点
Element root = document.createElement("members");
document.appendChild(root); // 添加一级节点
Element studentsElement = (Element)root.appendChild(document.createElement("students"));
Element teachersElement = (Element)root.appendChild(document.createElement("teachers")); // 添加二级节点并设置属性
Element studentElement1 = (Element)studentsElement.appendChild(document.createElement("student"));
studentElement1.setAttribute("name", "张三");
studentElement1.setAttribute("age", "18");
studentElement1.setAttribute("location", "河南");
Element studentElement2 = (Element)studentsElement.appendChild(document.createElement("student"));
studentElement2.setAttribute("name", "李四");
studentElement2.setAttribute("age", "26");
studentElement2.setAttribute("location", "新疆");
Element studentElement3 = (Element)studentsElement.appendChild(document.createElement("student"));
studentElement3.setAttribute("name", "王五");
studentElement3.setAttribute("age", "20");
studentElement3.setAttribute("location", "北京");
Element teacherElement1 = (Element)teachersElement.appendChild(document.createElement("teacher"));
teacherElement1.setAttribute("name", "zzs");
teacherElement1.setAttribute("age", "18");
teacherElement1.setAttribute("location", "河南");
Element teacherElement2 = (Element)teachersElement.appendChild(document.createElement("teacher"));
teacherElement2.setAttribute("name", "zzf");
teacherElement2.setAttribute("age", "26");
teacherElement2.setAttribute("location", "新疆");
Element teacherElement3 = (Element)teachersElement.appendChild(document.createElement("teacher"));
teacherElement3.setAttribute("name", "lt");
teacherElement3.setAttribute("age", "20");
teacherElement3.setAttribute("location", "北京"); // 获取文件对象
File file = new File("members.xml");
if(!file.exists()) {
file.createNewFile();
}
// 获取Transformer对象
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
// 设置编码、美化格式
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
// 创建DOMSource对象
DOMSource domSource = new DOMSource(document);
// 将document写出
transformer.transform(domSource, new StreamResult(new PrintWriter(new FileOutputStream(file))));
}

测试结果

此时,在项目路径下会生成 members.xml,文件内容如下,可以看到,使用w3c的 DOM 接口输出的内容没有缩进格式。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<members>
<students>
<student age="18" location="河南" name="张三"/>
<student age="26" location="新疆" name="李四"/>
<student age="20" location="北京" name="王五"/>
</students>
<teachers>
<teacher age="18" location="河南" name="zzs"/>
<teacher age="26" location="新疆" name="zzf"/>
<teacher age="20" location="北京" name="lt"/>
</teachers>
</members>

生成xml文件--使用dom4j的DOM接口

主要步骤

  1. 通过 DocumentHelper 获得 Document 对象,这个对象可以看成 xml 的树;

  2. 将对象转化为节点,并添加在 Document 这棵树上;

  3. 通过 XMLWriter 对象将树输出到文件中。

编写测试类

路径:test 目录下的cn.zzs.dom4j。通过对比,可以看出,dom4j 的 API 相比 JDK 的还是要方便很多。

注意:因为使用的是 dom4j 的 DOM 接口,所以节点对象导的是org.dom4j包,而不是org.w3c.dom包(dom4j 一个很大的特点就是改造了w3c的 DOM 接口,极大地简化了我们对节点的操作)。

    @Test
public void test02() throws Exception {
// 创建Document对象
Document document = DocumentHelper.createDocument(); // 添加根节点
Element root = document.addElement("members"); // 添加一级节点
Element studentsElement = root.addElement("students");
Element teachersElement = root.addElement("teachers"); // 添加二级节点并设置属性,dom4j改造了w3c的DOM接口,极大地简化了我们对节点的操作
studentsElement.addElement("student").addAttribute("name", "张三").addAttribute("age", "18").addAttribute("location", "河南");
studentsElement.addElement("student").addAttribute("name", "李四").addAttribute("age", "26").addAttribute("location", "新疆");
studentsElement.addElement("student").addAttribute("name", "王五").addAttribute("age", "20").addAttribute("location", "北京");
teachersElement.addElement("teacher").addAttribute("name", "zzs").addAttribute("age", "18").addAttribute("location", "河南");
teachersElement.addElement("teacher").addAttribute("name", "zzf").addAttribute("age", "26").addAttribute("location", "新疆");
teachersElement.addElement("teacher").addAttribute("name", "lt").addAttribute("age", "20").addAttribute("location", "北京"); // 获取文件对象
File file = new File("members.xml");
if(!file.exists()) {
file.createNewFile();
}
// 创建输出格式,不设置的话不会有缩进效果
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
// 获得XMLWriter
XMLWriter writer = new XMLWriter(new FileWriter(file), format);
// 打印Document
writer.write(document);
// 释放资源
writer.close();
}

测试结果

此时,在项目路径下会生成members.xml,文件内容如下,可以看出 dom4j 输出文件会进行缩进处理,而JDK的不会:

<?xml version="1.0" encoding="UTF-8"?>

<members>
<students>
<student name="张三" age="18" location="河南"/>
<student name="李四" age="26" location="新疆"/>
<student name="王五" age="20" location="北京"/>
</students>
<teachers>
<teacher name="zzs" age="18" location="河南"/>
<teacher name="zzf" age="26" location="新疆"/>
<teacher name="lt" age="20" location="北京"/>
</teachers>
</members>

使用例子--解析xml文件

需求

  1. 解析xml:解析上面生成的xml文件,将学生和老师节点按以下格式遍历打印出来(当然也可以再封装成对象返回给调用者,这里就不扩展了)。
student:name=张三,location=河南,age=18
student:name=李四,location=新疆,age=26
student:name=王五,location=北京,age=20
teacher:name=zzs,location=河南,age=18
teacher:name=zzf,location=新疆,age=26
teacher:name=lt,location=北京,age=20
  1. dom4j 结合 XPath 查找指定节点

主要步骤

  1. 通过 SAXReader 对象读取和解析xml文件,获得 Document 对象,即xml树;

  2. 调用 Node 的方法遍历打印xml树的节点;

  3. 使用 XPath 查询指定节点。

测试遍历节点

考虑篇幅,这里仅给出一种节点遍历方式,项目源码中还给出了其他的几种。

    /**
* 测试解析xml
*/
@Test
public void test03() throws Exception {
// 创建指定文件的File对象
File file = new File("members.xml");
// 创建SAXReader
SAXReader saxReader = new SAXReader();
// 将xml文件读入成document
Document document = saxReader.read(file);
// 获得根元素
Element root = document.getRootElement();
// 递归遍历节点
list1(root);
} /**
* 递归遍历节点
*/
private void list1(Element parent) {
if(parent == null) {
return;
}
// 遍历当前节点属性并输出
printAttr(parent);
// 递归打印子节点
Iterator<Element> iterator2 = parent.elementIterator();
while(iterator2.hasNext()) {
Element son = (Element)iterator2.next();
list1(son);
}
}

测试结果如下:

-------第一种遍历方式:Iterator+递归--------
student:name=张三,location=河南,age=18
student:name=李四,location=新疆,age=26
student:name=王五,location=北京,age=20
teacher:name=zzs,location=河南,age=18
teacher:name=zzf,location=新疆,age=26
teacher:name=lt,location=北京,age=20

测试XPath获取指定节点

    @Test
public void test04() throws Exception {
// 创建指定文件的File对象
File file = new File("members.xml");
// 创建SAXReader
SAXReader saxReader = new SAXReader();
// 将xml文件读入成document
Document document = saxReader.read(file);
// 使用xpath随机获取节点
List<Node> list = document.selectNodes("//members//students/student");
// List<Node> list = xmlParser.getDocument().selectSingleNode("students");
// 遍历节点
Iterator<Node> iterator = list.iterator();
while(iterator.hasNext()) {
Element element = (Element)iterator.next();
printAttr(element);
}
}

测试结果如下:

student:age=18,location=河南,name=张三
student:age=26,location=新疆,name=李四
student:age=20,location=北京,name=王五

XPath语法

利用XPath获取指定节点,平时用的比较多,这里列举下基本语法。

表达式 结果
/members 选取根节点下的所有members子节点
//members 选取根节点下的所有members节点
//students/student[1] 选取students下第一个student子节点
//students/student[last()] 选取students下的最后一个student子节点
//students/student[position()❤️] 选取students下前两个student子节点
//student[@age] 选取所有具有age属性的student节点
//student[@age='18'] 选取所有age属性为18的student节点
//students/* 选取students下的所有节点
//* 选取文档中所有节点
//student[@*] 选取所有具有属性的节点
//members/students\ //members/teachers

源码分析

本文会先介绍dom4j如何将xml元素抽象成具体的对象,再去分析dom4j解析xml文件的过程(注意,阅读以下内容前需要了解和使用过JDK自带的DOMSAX)。

dom4j节点的类结构

先来看下一个完整xml的元素组成,可以看出,一个xml文件包含了DocumentElementCommentAttributeDocumentTypeText等等。

DOM的思想就是将xml元素解析为具体对象,并构建树形数据结构。基于此,w3c提供了xml元素的接口规范,dom4j基本借用了这套规范(如下图),只是改造了接口的方法,使得我们操作时更加简便。

SAXReader.read(File file)

通过使用例子可知,我们解析xml文件的入口是SAXReader对象的read方法,入参可以是文件路径、url、字节流、字符流等,这里以传入文件路径为例。

注意:考虑篇幅和可读性,以下代码经过删减,仅保留所需部分。

    public Document read(File file) throws DocumentException {
//不管是URI,path,character stream还是byte stream,都会包装成InputSource对象
InputSource source = new InputSource(new FileInputStream(file));
if (this.encoding != null) {
source.setEncoding(this.encoding);
} //下面这段代码是为了设置systemId,当传入URI且没有指定字符流和字节流时,可以通过systemId去连接URL并解析
//如果一开始传入了字符流或字节流,这个systemId就是可选的
String path = file.getAbsolutePath();
if (path != null) {
StringBuffer sb = new StringBuffer("file://");
if (!path.startsWith(File.separator)) {
sb.append("/");
}
path = path.replace('\\', '/');
sb.append(path);
source.setSystemId(sb.toString());
} //这里调用重载方法解析InputSource对象
return read(source);
}

SAXReader.read(InputSource in)

看到这个方法的代码时,使用过JDK的SAX的朋友应该很熟悉,没错,dom4j也是采用事件处理的机制来解析xml。其实,只是这里设置的SAXContentHandler已经实现好了相关的方法,这些方法共同完成一件事情:构建xml树。明白这一点,应该就能理解dom4j是如何解决SAXDOM的缺点了。

注意:考虑篇幅和可读性,以下代码经过删减,仅保留所需部分。

    public Document read(InputSource in) throws DocumentException {
// 这里会调用JAXP接口获取XMLReader实现类对象
XMLReader reader = getXMLReader();
reader = installXMLFilter(reader); // 下面这些操作,是不是和使用JDK的SAX差不多,dom4j也是使用了事件处理机制。 // EntityResolver:通过实现resolveEntity方法,当解析xml需要引入外部数据源时触发,可以重定向到本地数据源或进行其他操作。
EntityResolver thatEntityResolver = this.entityResolver;
if (thatEntityResolver == null) {
thatEntityResolver = createDefaultEntityResolver(in
.getSystemId());
this.entityResolver = thatEntityResolver;
}
reader.setEntityResolver(thatEntityResolver); // 下面的SAXContentHandler继承了DefaultHandler,即实现了EntityResolver, DTDHandler, ContentHandler, ErrorHandler等接口
// 其中最重要的是ContentHandler接口,通过实现startDocument、endDocument、startElement、endElement等方法,当dom4j解析xml文件到指定元素类型时,可以触发我们自定义的方法。
// 当然,dom4j已经实现了ContentHandler的方法。具体实现的方法内容为:在解析xml时构建xml树
SAXContentHandler contentHandler = createContentHandler(reader);
contentHandler.setEntityResolver(thatEntityResolver);
contentHandler.setInputSource(in);
boolean internal = isIncludeInternalDTDDeclarations();
boolean external = isIncludeExternalDTDDeclarations();
contentHandler.setIncludeInternalDTDDeclarations(internal);
contentHandler.setIncludeExternalDTDDeclarations(external);
contentHandler.setMergeAdjacentText(isMergeAdjacentText());
contentHandler.setStripWhitespaceText(isStripWhitespaceText());
contentHandler.setIgnoreComments(isIgnoreComments());
reader.setContentHandler(contentHandler); configureReader(reader, contentHandler); // 使用事件处理机制解析xml,处理过程会构建xml树
reader.parse(in);
// 返回构建好的xml树
return contentHandler.getDocument();
}

SAXContentHandler

通过上面的分析,可知SAXContentHandlerdom4j构建xml树的关键。这里看下它的几个重要方法和属性。

startDocument()

    // xml树
private Document document; // 节点栈,栈顶存放当前解析节点(节点解析结束)、或当前解析节点的父节点(节点解析开始)
private ElementStack elementStack; // 节点处理器,可以看成节点开始解析或结束解析的标志
private ElementHandler elementHandler; // 当前解析节点(节点解析结束)、或当前解析节点的父节点(节点解析开始)
private Element currentElement;
public void startDocument() throws SAXException {
document = null;
currentElement = null; // 清空节点栈
elementStack.clear();
// 初始化节点处理器
if ((elementHandler != null)
&& (elementHandler instanceof DispatchHandler)) {
elementStack.setDispatchHandler((DispatchHandler) elementHandler);
} namespaceStack.clear();
declaredNamespaceIndex = 0; if (mergeAdjacentText && (textBuffer == null)) {
textBuffer = new StringBuffer();
} textInTextBuffer = false;
}

startElement(String,String,String,Attributes)

    public void startElement(String namespaceURI, String localName,
String qualifiedName, Attributes attributes) throws SAXException {
if (mergeAdjacentText && textInTextBuffer) {
completeCurrentTextNode();
} QName qName = namespaceStack.getQName(namespaceURI, localName,
qualifiedName);
// 获取当前解析节点的父节点
Branch branch = currentElement; if (branch == null) {
branch = getDocument();
}
// 创建当前解析节点
Element element = branch.addElement(qName);
addDeclaredNamespaces(element); // 添加节点属性
addAttributes(element, attributes); //将当前节点压入节点栈
elementStack.pushElement(element);
currentElement = element;
entity = null; // fixes bug527062 //标记节点解析开始
if (elementHandler != null) {
elementHandler.onStart(elementStack);
}
}

endElement(String, String, String)

    public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
if (mergeAdjacentText && textInTextBuffer) {
completeCurrentTextNode();
}
// 标记节点解析结束
if ((elementHandler != null) && (currentElement != null)) {
elementHandler.onEnd(elementStack);
}
// 当前解析节点从节点栈中弹出
elementStack.popElement();
// 指定为栈顶节点
currentElement = elementStack.peekElement();
}

endDocument()

    public void endDocument() throws SAXException {
namespaceStack.clear();
// 清空节点栈
elementStack.clear();
currentElement = null;
textBuffer = null;
}

以上,dom4j的源码分析基本已经分析完,其他具体细节后续再做补充。

参考资料:

浅析SAX,DOM,JAXP,JDOM与DOM4J之间的关系

相关源码请移步:https://github.com/ZhangZiSheng001/dom4j-demo

本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/11917301.html

dom4j的测试例子和源码详解(重点对比和DOM、SAX的区别)的更多相关文章

  1. cglib测试例子和源码详解

    目录 简介 为什么会有动态代理? 常见的动态代理有哪些? 什么是cglib 使用例子 需求 工程环境 主要步骤 创建项目 引入依赖 编写被代理类 编写MethodInterceptor接口实现类 编写 ...

  2. jdbc-mysql测试例子和源码详解

    目录 简介 什么是JDBC 几个重要的类 使用中的注意事项 使用例子 需求 工程环境 主要步骤 创建表 创建项目 引入依赖 编写jdbc.prperties 获得Connection对象 使用Conn ...

  3. DBCP2的使用例子和源码详解(不包括JNDI和JTA支持的使用)

    目录 简介 使用例子 需求 工程环境 主要步骤 创建项目 引入依赖 编写jdbc.prperties 获取连接池和获取连接 编写测试类 配置文件详解 数据库连接参数 连接池数据基本参数 连接检查参数 ...

  4. [Spark内核] 第40课:CacheManager彻底解密:CacheManager运行原理流程图和源码详解

    本课主题 CacheManager 运行原理图 CacheManager 源码解析 CacheManager 运行原理图 [下图是CacheManager的运行原理图] 首先 RDD 是通过 iter ...

  5. [Qt Creator 快速入门] 第2章 Qt程序编译和源码详解

    一.编写 Hello World Gui程序 Hello World程序就是让应用程序显示"Hello World"字符串.这是最简单的应用,但却包含了一个应用程序的基本要素,所以 ...

  6. Spark Sort-Based Shuffle具体实现内幕和源码详解

    为什么讲解Sorted-Based shuffle?2方面的原因:一,可能有些朋友看到Sorted-Based Shuffle的时候,会有一个误解,认为Spark基于Sorted-Based Shuf ...

  7. Arouter核心思路和源码详解

    前言 阅读本文之前,建议读者: 对Arouter的使用有一定的了解. 对Apt技术有所了解. Arouter是一款Alibaba出品的优秀的路由框架,本文不对其进行全面的分析,只对其最重要的功能进行源 ...

  8. go map数据结构和源码详解

    目录 1. 前言 2. go map的数据结构 2.1 核心结体体 2.2 数据结构图 3. go map的常用操作 3.1 创建 3.2 插入或更新 3.3 删除 3.4 查找 3.5 range迭 ...

  9. HashMap源码详解与对比

    前几天工作忙得焦头烂额时,同事问了一下关于Map的特性,刹那间懵了一下,紧接着就想起来了一些关于Map的一些知识,因为只要涉及到Collection集合类时,就会谈及Map类,因此理解好Map相关的知 ...

随机推荐

  1. Swagger -- 解决日期不正确

    继  Swagger--解决日期格式显示为Unix时间戳格式 UTC格式 这篇博客解决的日期格式后又发现了一个问题 问题 查询出来的时间没有注意到足足少了8个小时,如图 解决 其实这个问题不是Swag ...

  2. Creator 2.2.0 终于等来了这款Shader组件神器!一招搞定Effect特效

    先看下视频演示: ShaderHelper2支持Creator 2.2.0 视频录完后才想起,还没在微信小游戏中测试,赶紧试试,下面是在微信开发者工具中的截图. 径向模糊 探照灯 提供了一个Shade ...

  3. SpringBoot整合MybatisPlus3.X之逻辑删除(三)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  4. Vue躬行记(5)——组件通信

    组件之间除了保持独立之外,还需要相互通信,本章将介绍几种通信的方式. 一.直接访问 Vue提供了三个实例属性,可直接访问父组件.子组件和根实例,如下所列. (1)$parent:父组件. (2)$ro ...

  5. 关于 mybatis 报invalid comparison: java.util.Arrays$ArrayList and java.lang.String异常

    今天碰到个问题,来记录下,希望可以帮助到大家 贴错误源码: 这是一个根据list集合的查找数据的 sql,在接收list的时候加了判断 list != ‘ ’ “”,引起了集合与Stirng类型的比较 ...

  6. Spring Boot 日志处理你还在用Logback?

    ▶ Log4j2 性能 https://logging.apache.org/log4j/2.x/performance.html ▶ Spring Boot 依赖与配置 Maven 依赖 <! ...

  7. 在线API文档管理工具Simple doc

    Simple doc是一个简易的文档发布管理工具,为什么要写Simple doc呢?主要原因还是github的wiki并不好用:没有目录结构,文章没有Hx标签索引,最悲剧的是文章编辑的时候不能直接图片 ...

  8. os模块操作文件

    os模块: path=os.path.join(os.path.dirname(os.path.dirname(__file__)),'images') path:运行脚本的当前文件下的上一个文件的地 ...

  9. 「考试」$5T$

    啊因为最近题实在是好啊,只能四五篇四五篇写了. T1. 括号序列的确简单. 当我们维护左右$cnt$后. 到一个左括号的地方的话. 答案就是:$$\sum\limits_{i=1}^{min(lc,r ...

  10. NOIP模拟测试40

    考试时打了三个正解(或者叫能A的算法?),但是最终一个都没有A. 比较失败的一次考试. T1.队长快跑 先打了70分的dp,然后发现这个式子可以优化,拿线段树搞一下就好了,发现考试已经过去1h了,决定 ...