Day05.XML、Dom4j、综合案例

反射

  • 认知:反射技术就是对类进行解剖,解剖出"构造器"、"成员变量"、"成员方法"
    • 构造器: 可以实例化对象
    • 成员变量:可以赋值、取值
    • 成员方法:调用方法
    • 大白话:不使用new关键字,可以实例化对象,可以访问对象中的成员
  • 反射技术,通常应用于:框架
  • 使用反射技术:
    • 核心点:Class类
      • Class类是什么呢?
        • JVM中只能执行.class字节码文件(Java程序中会存在大量的.class文件)
        • .class文件是通过类加载器读取到内存中,并基于这个.class文件创建出:Class对象
          • Class对象,就是指一个.class文件
      • Class类的作用
        • 通过Class对象,获取"构造器"、"成员方法"、"成员变量"
    • 步骤:
      • 1、获取Class对象
      • 2、基于Class对象,可以获取:构造器、成员方法、成员变量
        • 构造器:Constructor类
        • 成员方法:Method类
        • 成员变量:Field类
      • 3、使用构造器,调用newInstance(...)方法,来实例化对象
      • 4、使用成员方法,调用invoke(...)方法,来调用方法入栈执行
      • 5、使用成员变量,调用set(...)方法、get(...)方法,对变量进行赋值、取值

注解

  • 认知:

    • 注解单独使用,没有任何意义。
    • 通常注解会配合反射技术一起使用,常用于框架技术
  • 注解的定义:

    • public @interface 注解名{
          数据类型 属性名();
          数据类型 属性名() default 默认值;
          数据类型[] 属性名();
      }
      
  • 注解的使用:

    • @注解名
      public class 类{
          
          @注解名(属性名=值)
          private String 成员变量;
          
          @注解名    //@Test
          public void 成员方法(int 参数){
              
          }   
      }
      
  • 元注解:

    • 作用:限定注解的使用位置、注解的生命周期
    • @Target //指定注解的使用位置
    • @Retention //设定注解的生命周期
  • 注解解析(注解存在的意义)

    • //API:
      //判断某个对象(类、接口、成员变量、构造器、成员方法)上是否有使用注解
      boolean flag = isAnnotationPresent(注解.class)
          
      //获取某个对象上的注解对象
      注解名  对象 =   getAnnotation(注解.class)   
          
          
      数据类型 变量 = 注解对象.属性;    
      
  • 示例:

    • //前置铺垫:
      一张数据表   对应    类
      一行记录     对应    对象    
      一个字段     对应    成员变量
          
      create table t_student
      (
         sname varchar(20),
         sage  int
      );
      
      //类
      class Student{
          private String name;
          private int age;
      }
          
      
      //JDBC程序: (程序员自己编写)
      连接数据库
      创建数据库操作对象
      发送sql语句: select ...
      处理结果集
           while(rs.next()){
               
              age = rs.getInt("sage");
              name =rs.getString("sname");
               
               Student stu = new Student();
               stu.setName( name );
               stu.setAge( age );     
           }
      
      
      //框架技术: 反射+注解   (别人已经完成的)
      //1、自定义一些注解
      public @interface Entity{  //实体
          String table();  //表名
      }   
      public @interface property{ //属性
          String name();
      }
      //2、解析@Entity、@Property注解
       扫描某些包下的的类,发现这些类上有@Entity、@Property注解,就会利用反射
       利用反射技术: 获取 Student.class 对象 (Class对象)
                    解析 类上的@Entity注解
       利用反射技术: 获取所有的成员变量 : 
                    解析所有成员变量上的@Property注解
                    
      
           
      
      //JDBC程序中使用框架技术中注解 ( 程序员自己编写 )
      
      @Entiry(table="t_student")  //表示当前的Student类和数据表t_student关联
      class Student{
          
          @Property(name="sname") //表示当前的成员变量name和字段sname关联
          private String name;
          
          @Property(name="sage") //表示当前的成员变量age和字段sage关联
          private int age;
      }
      
      
      

动态代理

  • 动态代理,提供了一个代理的对象,有了代理对象后,当访问某个方法时,会被代理对象拦截(拦截后可以对方法进行前增强、后增强【代理对象不会破坏原有方法的代码】)

  • 动态代理的特点:

    • 动态的创建.class文件
    • 动态的加载.class文件到内存中(创建Class对象)
      • Proxy类需要一个"类加载器"
    • 对程序员来讲,不需要书写代理类
  • 动态代理的代码实现:

    • 代理对象 = Proxy.newProxyInstance(类加载器 , 父接口 , 处理器)
      
    • 类加载器: 动态的加载.class文件
      
      父接口 : 代理类和被代理类需要拥有共同的父接口
          
      处理器: 代理对象拦截了方法后,对方法进行前增强、后增强,是由处理器来书写逻辑    
      
    • 代理对象  = Proxy.newProxyInstance(
                   类.class.getClassLoader(), //类加载器
                   被代理类.class.getInterfaces(), //父接口
                   
                   new InvocationHandler(){
                       public Object invoke(
                                             Object 代理对象, 
                                             Method 被拦截的方法对象 ,
                                             Object[] 方法中的实参
                                            ){
                           
                           
                           //业务逻辑
                       }
                          
                   }
                 )
      

XML,Dom4j、综合案例

今日内容

  • XML

  • Dom4j(解析xml文件)

  • 综合案例

教学目标

今日重点

1.书写xml
2.理解如何解析xml

第一章 XML

1 XML概述

目标

  1. 什么是XML

什么是XML

  1. 英文:eXtensible Markup Language 可扩展的标记语言,由各种标记(标签,元素)组成。
  2. 可扩展:所有的标签都是自定义的,可以随意扩展的。如:<abc/>,<姓名>
  3. 标记语言:整个文档由各种标签组成。清晰,数据结构化!
  4. XML是通用格式标准,全球所有的技术人员都知道这个东西,都会按照XML的规范存储数据,交互数据!!

XML作用

作用:总体上来说,就是为了存储维护数据的。

  1. 数据交换:不同的计算机语言之间,不同的操作系统之间,不同的数据库之间,进行数据交换。
    1552221268464

  2. 配置文件:在后期我们主要用于各种框架的配置文件基本天天见。

比如我们很快会学到连接池:c3p0-config.xml

1552221310087

小结

  1. xml是什么?

    • 可扩展的标记语言
      • 标记:标签。例:
    • xml文件是由N多个标签组成的
  2. 主要有哪两个作用?

  • 存储数据
  • 配置文件

2 编写第1个XML文件

需求

编写xml文档,用于描述人员信息,person代表一个人员,id是人员的属性代表人员编号。人员信息包括age年龄、name姓名、sex性别信息。

使用Java类去描述:

class Person{
  String id;
  int age;
  String name;
  String sex;
}

Person p = new Person("1","张三",18,"男");

效果

1552352318788

步骤

  1. 选择当前项目鼠标右键新建

    新建一个File命名时,以 .xml结尾。这个文件就是xml文件

1552221478628

  1. 编写person.xml文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <peopel>
        <person>
            <id>1</id>
            <name>张三</name>
            <age>18</age>
            <sex>男</sex>
        </person>
        
        <person>
            <id>2</id>
            <name>李四</name>
            <age>20</age>
            <sex>女</sex>
        </person>
    
    </peopel>
    
  2. 通过浏览器解析XML的内容

    1552221534649

  • 注:XML以后通过Java来进行解析,很少直接在浏览器上显示。

小结:

  • xml文件,通常以.xml作为后缀名
  • xml文件中的首行必须书写:
  • xml文件通常是由成对标签(开始标签、结束标签)组成

3 XML的组成:声明和元素

目标

  1. XML文档中的声明
  2. XML文档中的元素

XML组成

  1. 声明
  2. 元素(标签)
  3. 属性
  4. 注释
  5. 转义字符【实体字符】
  6. CDATA 字符区

文档声明

<?xml version="1.0" encoding="utf-8" ?>    固定格式
  1. IDEA会自动提示。

  2. 文档声明必须为<?xml开头,以?>结束

  3. 文档声明必须从文档的1行1列位置开始,必须在xml文档中的首行首列

  4. 文档声明中常见的两个属性:

    • version:指定XML文档版本。必须属性,这里一般选择1.0;
    • encoding:指定当前文档的编码,可选属性,默认值是utf-8;

元素(标签、标记)

格式1:  <person> 标签体 </person>   有标签体的标签
格式2:  <person/>  没有标签体的标签 (空标签)
  1. 元素是XML文档中最重要的组成部分;

  2. 普通元素的结构由开始标签、元素体、结束标签组成。【格式1】

  3. 元素体:元素体可以是元素,也可以是文本,例如:

    <person> 
      标签中可以包含另一个标签  
      <name>张三</name>  
    </person>
    
  4. 空元素:空元素只有标签,而没有结束标签,但元素必须自己闭合,例如:

    <sex/>
    
  5. 元素命名

    • 区分大小写
    • 不能使用空格,不能使用冒号
    • 不建议以XML、xml、Xml开头
    • 标签名不能数字开头,可以有数字
    • 可以使用下划线

    可以保持与Java命名标识符一样的规则

  6. 格式化良好的XML文档,有且仅有一个根元素。

错误演示:

元素没有结束

1552352828511

元素大写小写不一致

1552352880848

xml中多个根

1552352959286

小结

  1. 声明有哪两个常用的属性?

    • version
    • encoding
  2. 一个XML有几个根元素?

    • 有且只能有一个根元素
  3. XML标签命名不能有什么符号?

    • 不能使用关键字xml、XML
    • 不能有空格、不能有冒号
    • 不能以数字作为开头

4 XML的组成:属性、注释和转义字符

属性的语法

<person id="110">
  1. 属性是元素的一部分,它必须出现在元素的开始标签中

  2. 属性的定义格式:属性名=“属性值”,其中属性值必须使用单引或双引号括起来

  3. 一个元素可以有0~N个属性,但一个元素中不能出现同名属性

  4. 属性名不能使用空格 , 建议不要使用冒号等特殊字符,且必须以字母开头

    建议以Java的标识符定义规则做参考

<person id="123">
	<name>张三</name>
</person>

注释

<!-- 注释内容 -->

<!-- 
注释内容 1
注释内容 2
-->

XML的注释与HTML相同,既以<!--开始,-->结束。不能嵌套。

Java中注释:

// 单行
/* */ 多行注释
/** */ 文档注释

XML注释:

<!-- 注释内容 -->
<!--<person>注释</person>-->  <!-- 快捷键:Ctrl+/ :可以将整行进行注释-->
<person>三生三世</person> <!-- 快捷键:Ctrl+Shift+/:局部注释-->

转义字符[实体字符]

​ XML中的实体字符与HTML一样。因为很多符号已经被文档结构所使用,所以在元素体或属性值中想使用这些符号就必须使用实体字符

1552353386585

字符 预定义的转义字符 说明
< &lt; 小于(less than)
> &gt; 大于(greater than)
" &quot; 双引号(quotation)
' &apos; 单引号(apostrophe)
& &amp; 和号(ampersand )

注意:严格地讲,在 XML 中仅有字符 "<"和"&" 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。

转义字符应用示例:

​ 假如您在 XML 文档中放置了一个类似 "<" 字符,那么这个文档会产生一个错误,这是因为解析器会把它解释为新元素的开始。因此你不能这样写:

<message>if salary < 1000 then </message>

为了避免此类错误,需要把字符 "<" 替换为实体引用,就像这样:

<message>if salary &lt; 1000 then</message>

小结

  1. 属性必须出现在标签哪个位置?

    • 和开始标签绑定在一起,书写在开始标签元素的后面

    • <person id="属性值">
      </person>
      
  2. 同一个标签是否可以有同名的属性?

    • 不可能。
    • 允许有多个属性,属性之间使用空格分隔,但不能出现相同名称的属性
  3. 为什么要有转义字符(实体字符)?

    • 在xml文件中,一些特定的符号已经被xml使用了。例:> & 等
    • 希望在xml文档中,使用特定的符号,需要用:转义字符
      • < => &lt;
      • & => &amp;
  4. 注释的快捷?

    • ctrl + /

5 XML的组成:字符区(了解)

当大量的转义字符出现在xml文档中时,会使XML文档的可读性大幅度降低。这时如果使用CDATA段就会好一些。

CDATA (Character Data)字符数据区,格式如下:

<![CDATA[
	文本数据   < >  & ; " "
]]>
  1. CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)
  2. CDATA 部分由 <![CDATA[开始,由 ]]> 结束;

例如:

<![CDATA[
    if salary < 1000 then
]]

快捷模板:CD 回车

image-20200103105134226

注意:

​ CDATA 部分不能包含字符串 "]]>"。也不允许嵌套的 CDATA 部分。

​ 标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。

小结:

  • 字符区的特点:
    • 原样显示(书写的内容不会被xml解析器解析)

6 DTD约束(能够看懂即可)

1 什么是DTD

DTD(Document Type Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。

2 DTD约束的实现和语法规则(看懂dtd约束,书写符合规范的xml文件)

开发中,我们不会自己编写DTD约束文档,通常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档。

需求:接下来我们创建一个dtd约束文档,然后按照约束文档书写符合规范的xml文件。

我们先新建一个books.xml文件。

第一步:在项目下面创建一个dtd的文件夹,然后选中文件夹,鼠标右击,新创键一个books.xml文件

第二步:我们先书写books.xml文件中的内容:

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>面试宝典</name>
		<author>锁哥</author>
		<price>78.8</price>
	</book>
	<book>
		<name>java从入门到精通</name>
		<author>黑旋风</author>
		<price>88.8</price>
	</book>
</books>

经过上述四步我们就将books.xml文件书写完毕,接下来我们开始书写DTD约束。

关于DTD约束我们能够看懂即可。如下所示就是上述books.xml文件引入了DTD约束。

简单的DTD约束就写好了,如下所示:

我们直接将如下约束复制到我们上述书写好的books.xml文件中即可,能够读懂即可。

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
	1.在xml内部定义dtd约束的声明 : <!DOCTYPE 根元素 [元素声明]>
	2.xml的元素(标签)的声明: <!ELEMENT 元素名称 (元素内容)>
		<!ELEMENT books (book)> 表示books标签下是book标签
	    <!ELEMENT book (name,author,price)> 表示的是book标签下出现的name,author,price子标签
	    <!ELEMENT name (#PCDATA)> 表示name标签中出现的内容是文本
 -->
 <!DOCTYPE books [
 	<!--约束根标签 book* 表示books标签下可以有多个book子标签
 		* + ? 和正则表达式中表示的意思是一样的
		* :  0 1 n
		+ : 1 n
		? :  0 1
 	-->
 	<!ELEMENT books (book*)>
 	<!--约束book标签-->
 	<!ELEMENT book (name,author,price)>
 	<!--约束name,author,price标签 
 		但是这三个标签下就是文本了
 		#PCDATA 表示标签下内容是文本
 	-->
 	<!ELEMENT name (#PCDATA)>
 	<!ELEMENT author (#PCDATA)>
 	<!ELEMENT price (#PCDATA)>
 ]>
<books>
	<book>
		<name>面试宝典</name>
		<author>锁哥</author>
		<price>78.8</price>
	</book>
	<book>
		<name>java从入门到精通</name>
		<author>黑旋风</author>
		<price>88.8</price>
	</book>
</books>

说明:

1)xml中出现的标签,也叫做元素。那么我们书写的约束可以规范xml中到底能出现哪些标签。除此之外都不能出现。

所以xml中出现的标签都需要我们开发者在dtd约束中声明一下。只有声明了这个标签,xml中才能出现这个标签。如果约束中没有声明,那么xml中就不能出现。

所以,xml中出现的标签需要使用如下的语法(也就是xml中元素的声明语法)。

在xml内部定义dtd约束的声明 :

 <!DOCTYPE 根元素 [元素声明]>

xml的元素(标签)的声明:

<!ELEMENT 元素名称 (元素内容)>

元素名:自定义。

元素内容包括:符号、数据类型和标签。

常见符号:? * + () |

常见数据类型:#PCDATA 表示内容是文本,不能是子标签。

标签:就是正常子标签。

2)由于 顺序是name,author,price,所以下面的顺序也得是:

<book>
		<name>面试宝典</name>
		<author>锁哥</author>
		<price>78.8</price>
</book>

前后顺序不能换。

接下来对上述代码的标签添加属性,比如给book属性添加一个address地址,表示将书存放到哪里。

添加属性之后的代码如下所示:

<!DOCTYPE books [
 	<!--约束根标签 book* 表示books标签下可以有多个book子标签
 		* + ? 和正则表达式中表示的意思是一样的
 	-->
 	<!ELEMENT books (book*)>
 	<!--约束book标签-->
 	<!ELEMENT book (name,author,price)>
 	<!--约束name,author,price标签 
 		但是这三个标签下就是文本了
 		#PCDATA 表示标签下内容是文本
 	-->
 	<!ELEMENT name (#PCDATA)>
 	<!ELEMENT author (#PCDATA)>
 	<!ELEMENT price (#PCDATA)>
 	<!ATTLIST book address CDATA "图书馆"
 					id      ID    #REQUIRED
 			>
 ]>
<books>
	<book address="藏经阁" id="a1">
		<name>面试宝典</name>
		<author>锁哥</author>
		<price>78.8</price>
	</book>
	<book id="a2">
		<name>java从入门到精通</name>
		<author>黑旋风</author>
		<price>88.8</price>
	</book>
</books>

对上述声明属性进行解释说明:

元素名称: 表示该属性使用在哪个标签上;

属性名称: 表示在标签上添加的属性名字;

属性类型: 添加的属性类型。

属性类型有如下几种:

​ 类型 描述

​ **CDATA 值为字符数据 (character data) **

​ (en1|en2|..) 此值是枚举列表中的一个值

​ **ID 值为唯一的 id **

默认值: 表示最开始给属性的默认值。

​ 值 解释

​ 值 属性的默认值

​ **#REQUIRED 属性值是必需的 **

​ #IMPLIED 属性不是必需的

​ #FIXED value 属性值是固定的

7 Schema约束(能够看懂即可)

与dtd约束一样,schema它也是用来约束xml文件的。schema约束书写的时候,它遵守xml的语法规则。在书写schema的时候,就和书写xml文件的文档结构一样。

注意:书写schema文件的时候,它的文件扩展名是xsd。

1 书写schema约束

1、首先创建一个xml文件。然后根据xml文件书写符合规范的schema约束。

创建books.xml文件:

代码如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>面试宝典</name>
		<author>锁哥</author>
		<price>78.8</price>
	</book>
	<book>
		<name>java从入门到精通</name>
		<author>黑旋风</author>
		<price>88.8</price>
	</book>
</books>

2、接下来我们需要创建一个schema文件:

步骤:

第一步:首先进入文件创建窗口,选中工程鼠标右击,new-->File,然后进入如下页面:

第二步:输入创建的文件的名称和schema文件的后缀名xsd;

第三步:因为schema约束文件本身就是xml,所以声明xml文件的头适用于schema约束的文件。

第四步:将如下的内容复制到上述已经创建好的books.xsd中。然后我们就可以书写schema约束了。

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
		   targetNamespace="http://www.example.org/books"
		   elementFormDefault="qualified">
</schema>

说明:

1)schema约束的结构和xml文件书写规范是一样的,表示对xml的声明,是根标签;

2)xmlns="http://www.w3.org/2001/XMLSchema" 表示此schema文件受到w3组织的指定的约束;

3)targetNamespace="http://www.example.org/books",叫做名称空间,这个相当于java中包的作用,区分不同约束中的不同标签。当需要被当前这个schema文件约束的xml文件,需要通过当前这个名字引入当前schema文件。

4)elementFormDefault="qualified",如果值为qualified,那么当前schema中所有的标签默认都是放在名称空间中的。如果值为unqualified,那么除了schema中的根标签在名称空间即 http://www.example.org/books 包中,其他的标签都不会在这个包中。在开发中,我们都是书写默认值qualified。

上述了解完成之后,接下来我们读一下一个完整的schema约束:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
		targetNamespace="http://www.example.org/books"
		elementFormDefault="qualified">
		<!-- 在此处书写schema约束语法 -->
		<element name="books">
			<complexType><!-- 声明books是复杂标签 -->
				<sequence><!-- 通过sequence标签指定子标签的顺序 -->
					<element name="book" maxOccurs="unbounded">
						<complexType>
							<sequence>
								<element name="name" type="string"></element>
								<element name="author" type="string"></element>
								<element name="price" type="double"></element>
							</sequence>
							<attribute name="address"></attribute>
						</complexType>
					</element>
				</sequence>
			</complexType>
		</element>
</schema>

说明:

1.xml中出现的标签需要使用这样的语法来定义。即先清楚xml中需要多少个标签,在Schema文件中就书写多少个element标签。

所以xml中第一个出现的books标签需要使用来声明。

element标签中的 name属性 就是xml中 可以书写的标签的名字。

2.为了方便schema约束的书写,我们将xml中的标签简单的分为2大类:

​ a) 简单标签 : 标签中只有文本数据;

​ b) 复杂标签:标签中有子标签或者属性加上文本数据;

在element标签中需要使用complexType声明当前的element标签name属性指定的是一个复杂标签。

如果是简单标签可以使用simpleType。

3.上述在books.xml文件中我们发现books标签是复杂标签,针对复杂标签,需要在当前的标签中书写子标签来限制当前复杂标签中的其他内容。

所以我们需要使用标签来声明books标签是一个复杂标签。并且针对复杂标签中出现的子标签,我们需要使用标签来声明子标签出现的顺序。

A)books标签中出现的子标签是book,由于book也是一个标签,所以我们也需要使用标签来声明xml中的book;

B)并且book标签也是一个复杂标签,所以我们需要使用标签来声明;

C)book标签中也有子标签,所以还需要使用来声明子标签的顺序;

D)最后发现book标签在books.xml出现了多次,所以需要给加上maxOccurs="unbounded"属性。表示book标签可以出现很多次。(没有次数的限制)

大于等于1次

5.最后在里面写上book标签中出现的3个name,author,price子标签的声明。并且针对book标签中出现的属性。我们需要使用

这样的语法来声明。注意标签的位置。

在books.xml文件中增加一个address属性:

所以在books.xsd即schema约束中添加一个属性。注意标签的位置。

2 在xml文件中引入schema约束

在books.xml文件中引入schema约束的步骤:

第一步:首先鼠标放在根标签books后面,打个空格,然后将如下内容复制到books后面

代码如下:

<books xmlns="default namespace"
		xsi:schemaLocation="{namespace} {location}"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

然后引入schema约束的图解

最终完整代码:

<?xml version="1.0" encoding="UTF-8"?>
<books xmlns="http://www.example.org/books"
		xsi:schemaLocation="http://www.example.org/books books.xsd"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		>
	<book address="藏经阁">
		<name>面试宝典</name>
		<author>锁哥</author>
		<price>78.8</price>
	</book>
	<book>
		<name>java从入门到精通</name>
		<author>黑旋风</author>
		<price>88.8</price>
	</book>
</books>

说明:

1)xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance",表示当前xml文件是一个受schema约束的实例,这里不需要改变;

2)xmlns="http://www.example.org/books",schema约束文件中的targetNamespace的值,表示在books.xml文件中引入名称空间;

3)xsi:schemaLocation="http://www.example.org/books books.xsd",

schema约束文件中的targetNamespace的值 schema约束文件的路径

第二章 Dom4j

1 XML解析

1.1 解析概述

​ 当将数据存储在XML后,我们就希望通过程序获取XML的内容。我们使用Java基础所学的IO知识是可以完成的,不过需要非常繁琐的操作才可以完成,且开发中会遇到不同问题(只读、读写)。

人们为不同问题提供不同的解析方式,使用不同的解析器进行解析,方便开发人员操作XML。

1.2 解析方式和解析器

开发中比较常见的解析方式有三种,如下:

  1. DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象

    a)优点:元素与元素之间保留结构关系,故可以进行增删改查操作。

    b)缺点:XML文档过大,可能出现内存溢出

  2. SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都触发对应的事件。

    a)优点:处理速度快,可以处理大文件

    b)缺点:只能读,逐行后将释放资源,解析操作繁琐。

  3. PULL:Android内置的XML解析方式,类似SAX。(了解)

解析器,就是根据不同的解析方式提供具体实现。有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包

常见的解析器

1552305195234

2 Dom4j的基本使用

2.1 DOM解析原理及结构模型

解析原理

将整个XML文档加载到内存,生成一个DOM树,并获得一个Document对象,通过Document对象就可以对DOM树进行操作。以下面books.xml文档为例。

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book id="0001"> 
        <name>JavaWeb开发教程</name>
        <author>张孝祥</author>
        <sale>100.00元</sale>
    </book>
    <book id="0002">
        <name>三国演义</name>
        <author>罗贯中</author>
        <sale>100.00元</sale>
    </book>
</books>

结构模型

DOM中的核心概念就是节点,在XML文档中的元素、属性、文本,在DOM中都是节点!所有的节点都封装到了Document对象中。

结论:使用Document对象,就可以去访问DOM树中的每一个节点

引入dom4j的jar包

去官网下载 zip 包。http://www.dom4j.org/
1552305595501

通常我们会在项目中创建lib文件夹,将需要依赖的库放在这里。

库导入方式:

  1. 在IDEA中,选择项目鼠标右键--->弹出菜单-->open Module settings”-->Dependencies-->+-->JARs or directories... 找到dom4j-1.6.1.jar,成功添加之后点击"OK" 即可。

  2. 直接右键选择:Add as Library

    image-20200719205315975

小结

dom4j的解析思想,先把xml文档加载到内存中,从而得到一个DOM树,并创建一个Document对象去维护dom树。

利用Document对象,就可以去解析DOM树(解析XML文件)

回顾上午内容:

  • xml文档
    • xml是由一些标签组成
    • xml的后缀名:.xml
    • xml文档的组成内容:
      • 文档声明(固定的)
      • 标签(标记、元素)
      • 属性
      • 注释
      • 转义字符
      • 字符区
    • xml:可扩展的标记语言。所有的标记都是自定义的(书写的随意性比较高)
    • xml约束:约束xml文档
      • dtd约束
      • schema约束
        • 比dtd约束更灵活、更强大(针对数据的类型也可以约束)
  • xml解析
    • 对xml文档的内容进行解析,解析出xml中的一些数据
    • 解析方式:
      • dom4j
        • 实现原理:
          • 把整个xml文档加载到内存中,并生成一个DOM树,基于dom树创建一个Document对象
          • 利用Document对象来对xml进行解析
      • xpath

2.2 常用的方法

dom4j 必须使用核心类SaxReader加载xml文档获得Document,通过Document对象获得文档的根元素,然后就可以操作了。

SAXReader对象
方法 作用
SAXReader sr = new SAXReader(); 构造器
Document read(String url) 加载执行xml文档
Document对象
方法 作用
Element getRootElement() 获得根元素
Element对象
方法 作用
List<Element> elements(String ele ) 获得指定名称的所有子元素。可以不指定名称
Element element(String ele) 获得指定名称第一个子元素。
String getName() 获得当前元素的元素名
String attributeValue(String attrName) 获得指定属性名的属性值
String elementText(Sting ele) 获得指定名称子元素的文本值
String getText() 获得当前元素的文本内容

小结

解析xml的步骤:

  1. 创建SaxReader对象,调用read方法关联xml文件,得到一个Document对象
  2. 通过Document对象,获取根元素
  3. 获取根元素之后,就可以层层深剥,运用Element相关的API进行解析其子元素

2.3 方法演示

复制资料下的常用xml中"books.xml",内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book id="0001">
        <name>JavaWeb开发教程</name>
        <author>张孝祥</author>
        <sale>100.00元</sale>
    </book>
    <book id="0002">
        <name>三国演义</name>
        <author>罗贯中</author>
        <sale>100.00元</sale>
    </book>
</books>

注意:为了便于解析,此xml中没有添加约束

解析此文件,获取每本书的id值,以及书本名称,作者名称和价格.

步骤分析:

  1. 创建一个SaxReader对象,调用read方法加载一个xml文件获得文档对象
  2. 通过文档对象,获取根元素
  3. 通过根元素一层一层的进行解析子元素。
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.util.List;

public class Demo01 {
    public static void main(String[] args) throws DocumentException {
        //1. 创建一个SaxReader对象,调用read方法加载一个xml文件获得文档对象
        SAXReader sr = new SAXReader();
        Document doc = sr.read("day15/xml/book.xml");

        //2. 通过文档对象,获取根元素
        Element rootElement = doc.getRootElement();

        //3. 通过根元素一层一层的进行解析子元素。
        //获取所有的子元素
        List<Element> bookElements = rootElement.elements("book");

        for (Element bookElement : bookElements) {
            //System.out.println(bookElement);
            //解析属性
            String id = bookElement.attributeValue("id");
            System.out.println("id = " + id);
            //获取子元素文本
            String name = bookElement.elementText("name");
            String author = bookElement.elementText("author");
            String sale = bookElement.elementText("sale");
            System.out.println("name = " + name);
            System.out.println("author = " + author);
            System.out.println("sale = " + sale);



            System.out.println("----------------------");
        }
        
    }
}

需求二:

将xml中文件数据解析成为java对象,每个book解析为一个book类型的对象。然后将book对象放到一个集合中存储。

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book id="0001">
        <name>JavaWeb开发教程</name>
        <author>张孝祥</author>
        <sale>100.00元</sale>
    </book>
    <book id="0002">
        <name>三国演义</name>
        <author>罗贯中</author>
        <sale>100.00元</sale>
    </book>
</books>

步骤分析:

  1. 先创建一个Book类对应book元素
  2. 创建一个ArrayList集合用来存储解析后的book对象
  3. 创建SaxReader对象,调用read方法加载xml文件,得到文档对象
  4. 通过文档对象获取根元素,然后层层解析

代码实现:

public class Demo02 {
    public static void main(String[] args) throws DocumentException {
        //定义一个集合用来存储解析的Book对象
        ArrayList<Book> books = new ArrayList<>();



        //1. 创建一个SaxReader对象,调用read方法加载一个xml文件获得文档对象
        SAXReader sr = new SAXReader();
        Document doc = sr.read("day15/xml/book.xml");

        //2. 通过文档对象,获取根元素
        Element rootElement = doc.getRootElement();

        //3. 通过根元素一层一层的进行解析子元素。
        //获取所有的子元素
        List<Element> bookElements = rootElement.elements("book");

        for (Element bookElement : bookElements) {
            //System.out.println(bookElement);
            //解析属性
            String id = bookElement.attributeValue("id");
            System.out.println("id = " + id);
            //获取子元素文本
            String name = bookElement.elementText("name");
            String author = bookElement.elementText("author");
            String sale = bookElement.elementText("sale");

            //将解析的字符串封装成为对象,放到集合
            Book book = new Book(id,name,author,sale);
            books.add(book);

        }


        //将集合遍历,打印book对象
        for (Book book : books) {
            System.out.println("book = " + book);
        }

    }
}

class Book{
    private String id;
    private String name;
    private String author;
    private String sale;

    public Book() {
    }

    public Book(String id, String name, String author, String sale) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.sale = sale;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getSale() {
        return sale;
    }

    public void setSale(String sale) {
        this.sale = sale;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", sale='" + sale + '\'' +
                '}';
    }
}

3 使用xpath技术结合DOM4J技术读取xml文件(了解)

1.概念介绍

问题:通过上面的案例我们发现有个小问题.就是获取标签的过程太过繁琐。我们需要一层一层的去获取。假设xml嵌套了50层的话,对于我们开发者来说是非常繁琐的。所以我们希望应该有这样的技术,一下子直接就能获取我们所需要的标签对象。这就是我们接下来需要学习的xpath技术。

xpath技术 也是 W3C 组织制定的快速获取 xml文件中某个标签的技术。

xpath其实就像在文件系统中定位文件,所以我们学习xpath主要学习xpath的路径表达式。

2.XPath使用步骤

步骤1:导入jar包(dom4j-1.6.1.jar(dom4j核心包)和jaxen-1.1-beta-6.jar(xpath依赖的包))

image-20200506193939941

步骤2:通过dom4j的SaxReader获取Document对象

image-20200506194111624

步骤3:使用document常用的api结合xpath的语法完成选取XML文档元素节点进行解析操作。

说明:

  • Node叫做节点,DOM里面所有的类型都是Node的子类
    • 比如Document Element Attribute 都是 Node的子类
  • Node中的两个方法可以使用XPath:
方法 作用
List selectNodes("表达式") 获取符合表达式的元素集合
Element selectSingleNode("表达式") 获取符合表达式的唯一元素

3.XPath语法(了解)

在学习如何使用XPath之前,我们先将课后资料中的tianqi.xml放到项目根目录下,然后我们在演示如何使用xpath。

3.1全文搜索路径表达式方式 掌握
  • 代表无论中间有多少层,直接获取所有子元素中满足条件的元素
    • 一个“/”符号,代表逐级写路径
    • 2个“//”符号,不用逐级写路径,可以直接选取到对应的节点,是全文搜索匹配的不需要按照逐层级 重点
  • 需求1:获取天气预报里的所有湿度,不论有多少层
  • 需求2:获取广州里面的3个最高温度
//代表无论中间有多少层,直接获取所有子元素中满足条件的元素
    @Test
    public void demo03() throws Exception {
        //全文搜索    //
        SAXReader reader = new SAXReader();
        Document document = reader.read("tianqi.xml");
        //获取天气预报里的所有湿度,不论有多少层
        List<Node> list = document.selectNodes("//湿度");

        //遍历打印
        for (Node node : list) {
            System.out.println(node.getText());
        }
        System.out.println("--------------");

        //获取广州里面的3个最高温度
        List<Node> list1 = document.selectNodes("/天气预报/广州//最高温度");
        for (Node node : list1) {
            System.out.println(node.getText());
        }
    }

第三章 综合案例

学习目标:

  • 理解所谓的框架是如何实现的,如何使用框架

1、需求

需求:自定义dao层jdbc框架

  • 为了方便程序员操作数据库,让程序员更关注于sql代码层面和业务层面

2、案例效果

image-20210610210837862

image-20210610210613141

image-20210610211138286

使用到的技术:

  • 反射
  • 注解
  • 动态代理
  • xml解析:xpath

3、案例分析

自定义jdbc框架开发步骤:

1、通过软配置方式,和数据库连接

  • 解析xml配置文件,获得:driver、url、username、password
  • 德鲁伊连接池
    • 根据配置文件中的参数,创建连接池对象

2、创建@Select注解

  • 解析@Select注解中value值:select查询语句

3、创建映射类Mapper

  • 把从@Select注解中解析出来的select查询语句,赋值给Mapper中的sql成员变量

4、创建SqlSession类

  • 提供getMapper()方法,用来获取代理对象
    • 说明:程序员在获取到代理对象后,利用代理对象调用某个方法时,会被代理对象拦截处理

4、自定义JDBC框架-代码实现

4.1、Configuration

/*配置类
  1. 解析XML文件
  2. 创建德鲁伊连接池
*/
public class Configuration {
    /* 定义数据库连接对象相关属性 */
    private String driver;//驱动
    private String url;//连接地址
    private String username;//登录名
    private String password;//密码

    /* Mapper接口的全名称 */
    private String interName;

    /* 数据库连接池对象 */
    private DataSource dataSource;

    /* 映射类对象 */
    private Mapper mapper = new Mapper();


    //无参构造方法
    public Configuration() {
        try {
            //解析"config.xml"文件
            SAXReader reader = new SAXReader();
            InputStream is = Configuration.class.getClassLoader().getResourceAsStream("config.xml");
            Document doc = reader.read(is);
            //调用自定义方法: 将核心配置文件中的数据封装到Configuration类的属性中
            loadConfigXml(doc);

            //调用自定义方法: 初始化数据库连接池对象
            createDataSource();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //初始化数据库连接池对象
    private void createDataSource() throws Exception {
        //创建c3p0核心类对象
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        //使用对象调用方法将四大连接参数给数据库连接池
        cpds.setDriverClass(driver);
        cpds.setJdbcUrl(url);
        cpds.setUser(username);
        cpds.setPassword(password);

        //将cpds赋值给成员变量ds
        this.dataSource = cpds;//数据库连接池对象
    }


    //将核心配置文件中的数据封装到Configuration类属性中
    private void loadConfigXml(Document doc) {
        /*
        //使用document对象调用方法获取property标签:
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/itheima"/>
        <property name="username" value="root"/>
        <property name="password" value="itheima"/>
        */
        List<Node> list = doc.selectNodes("//property");

        //遍历List集合
        for (Node node : list) {
            //强制转换
            Element e = (Element) node;
            //获取property标签的name属性值
            String name = e.attributeValue("name");//双引号中的name是property标签的name属性 driver
            //获取property标签的value属性值
            String value = e.attributeValue("value");//双引号中的value是property标签的value属性 com.mysql.jdbc.Driver
           
            //将value的值赋值给成员变量
            switch (name) {
                case "driver":
                    this.driver = value;//数据库驱动
                    break;
                case "url":
                    this.url = value;//连接url
                    break;
                case "username":
                    this.username = value;//登录名
                    break;
                case "password":
                    this.password = value;//密码
                    break;
            }
        }

        //<package name="xxx.xxx.UserMapper"></package>
        Node node = doc.selectSingleNode("//package");
        Element e = (Element) node;
        //Mapper接口的全名称
        this.interName = e.attributeValue("name");//"xxx.xxx.UserMapper"
    }


    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getInterName() {
        return interName;
    }

    public void setInterName(String interName) {
        this.interName = interName;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Mapper getMapper() {
        return mapper;
    }

    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }
}

4.2、注解

@Select

//查询时使用的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
    String[] value();
}

@ResultType

//查询结果的封装类型
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResultType {
    String value();
}

4.3、映射类:Mapper

package cn.itcast.config;

public class Mapper {
    private String sql;//存储sql语句
    private String resultType;//结果类型

    public Mapper() {
    }

    public Mapper(String sql) {
        this.sql = sql;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }
}

4.4、SqlSession类

@SuppressWarnings("all")
public class SqlSession {
    /**
     * 动态代理
     */
    public <T> T getMapper(Class<T> clazz) {
        //类加载器: 负责加载代理类到内存中
        ClassLoader classLoader = SqlSession.class.getClassLoader();

        //父接口
        Class[] interfaces = {clazz};


        T mapperProxy = (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {

            //在调用方法时,代理对象执行invoke方法,返回List
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //创建核心配置类对象
                Configuration config = new Configuration();


                /******** 解析 @Select、@ResultType *******/
                Class clazz = Class.forName(config.getInterName());

                Method[] methods = clazz.getMethods();
                //遍历数组
                for (Method m : methods) {
                    //判断是否有注解
                    boolean boo = m.isAnnotationPresent(Select.class);

                    boolean boo2 = m.isAnnotationPresent(ResultType.class);

                    if (boo && boo2) {
                        //获取@Select注解对象
                        Select select = m.getAnnotation(Select.class);
                        //获取属性值
                        String[] value = select.value();//{"select * from user"}
                        String sql = value[0];
                        //给Mapper对象中的sql成员变量赋值
                        config.getMapper().setSql(sql);

                        //获取@ResultType注解对象
                        ResultType resultType = m.getAnnotation(ResultType.class);
                        String type = resultType.value();//获取属性值
                        config.getMapper().setResultType(type);
                    }
                }
                /*******************************/


                //获取映射对象
                Mapper mapper = config.getMapper();
                //利用映射对象,获取sql语句
                String sql = mapper.getSql();

                //利用映射对象,获取类型
                String resultType = mapper.getResultType();
                Class cl = Class.forName(resultType);


                //获取数据库连接池对象
                DataSource ds = config.getDataSource();
                //利用连接池对象,获取Connection对象
                Connection conn = ds.getConnection();

                //调用自定义方法: 执行sql查询语句
                List list = queryForList(conn, sql, cl);

                return list;
            }
        });

        //代理对象返回给getMapper的调用者     UserMapper mapper = sqlSession.getMapper(UserMapper.class);//mapperProxy
        return mapperProxy;
    }
   public List queryForList(Connection conn, String sql, Class clazz) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        List userList = new ArrayList();

        //通过连接对象得到预编译的语句对象
        PreparedStatement ps = conn.prepareStatement(sql);

        //执行SQL语句,得到结果集
        ResultSet rs = ps.executeQuery();

        while (rs.next()) {
            //获取构造方法对象,并实例化
            Object user = clazz.getConstructor().newInstance();

            //获取所有的成员变量(包含私有成员变量)
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) { //field可以是:id name passwd age gender adddate
                //得到成员变量的名字
                String name = field.getName();

                //暴力破解: 取消权限检查
                field.setAccessible(true);

                //rs.getObject(name) 表示根据数据表的列名取出数据表中的列值 因为User类中的成员变量名必须和数据表列名一致
                //例如: name 的值是birthday 那么这里 rs.getObject(name)---》rs.getObject("age")获取数据表的年龄20
                Object table_value = rs.getObject(name);

                //void set(Object obj, Object value)给成员变量赋值,参数1:对象名 参数2:要赋的值
                field.set(user, table_value);

            }
            //user对象添加到集合中
            userList.add(user);
        }

        //释放资源
        rs.close();
        ps.close();
        conn.close();

        //返回集合
        return userList;
    }
}   

5、自定义JDBC框架的使用

5.1、数据表

CREATE TABLE user (
  id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name varchar(30) DEFAULT NULL,
  passwd varchar(20) DEFAULT NULL,
  age int(3) DEFAULT NULL,
  gender varchar(2) DEFAULT NULL,
  adddate date DEFAULT NULL
);

# 测试数据
INSERT INTO user VALUES (null, 'itcast', '123123', '10', '男', '2020-12-11');

5.2、创建实体类

public class User {
    private int id;
    private String name;
    private String passwd;
    private int age;
    private String gender;
    private Date adddate;

    public User() {
    }

    public User(int id, String name, String passwd, int age, String gender, Date adddate) {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
        this.age = age;
        this.gender = gender;
        this.adddate = adddate;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Date getAdddate() {
        return adddate;
    }

    public void setAdddate(Date adddate) {
        this.adddate = adddate;
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", passwd='" + passwd + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", adddate=" + adddate +
                '}';
    }
}

5.3、UserMapper接口

public interface UserMapper {

    //查询所有用户
    @Select("select * from user")
    @ResultType("com.itcast.pojo.User")
    public abstract List queryAllUser();

}

5.4、配置文件:config.xml

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

    <!--数据源-->
    <dataSource>
        <!--驱动-->
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <!--地址-->
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/itheima"/>
        <!--用户名-->
        <property name="username" value="root"/>
        <!--密码-->
        <property name="password" value="itheima"/>
    </dataSource>

    <!--加载映射接口-->
    <mappers>
        <package name="com.itcast.mapper.UserMapper"></package>
    </mappers>

</configuration>

5.5、测试类

public class Test1 {
    @Test
    public void testSelect() {
        //实例化
        SqlSession sqlSession = new SqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = mapper.queryAllUser();

        for (User u : users) {
            System.out.println(u);
        }
    }
}

今日作业

1.书写xml: person book

2.解析xml

3.熟悉综合案例流程:反射 动态代理 解析注解

4.预习

posted @ 2023-03-05 18:06  忘了鱼尾纱的猫  阅读(134)  评论(0)    收藏  举报