目录:
- 反射
- 注解
- 动态代理
- XML
- XML概述
- 目标
- 什么是XML
- XML作用
- 小结
- 编写第1个XML文件
- 需求
- 效果
- 步骤
- 小结:
- XML的组成:声明和元素
- 目标
- XML组成
- 文档声明
- 元素(标签、标记)
- 小结
- XML的组成:属性、注释和转义字符
- 属性的语法
- 注释
- 转义字符[实体字符]
- 小结
- XML的组成:字符区(了解)
- XML约束:DTD约束
- 目标
- XML约束
- 约束示例
- DTD学习要求
- 小结:
- XML约束:Schema约束
- 目标
- 概念
- 约束示例
- 实现步骤
- 名称空间
- schema学习要求
- 小结:
- XML概述
- Dom4j
- XML解析
- 解析方式和解析器
- 小结
- Dom4j的基本使用
- DOM解析原理及结构模型
- 解析原理
- 结构模型
- 引入dom4j的jar包
- 小结
- 常用的方法
- SAXReader对象
- Document对象
- Element对象
- 小结
- 方法演示
- DOM解析原理及结构模型
- Dom4J结合XPath解析XML(了解)
- 介绍
- XPath使用步骤
- XPath语法(了解)
- 绝对路径表达式(了解)
- 相对路径表达式(了解)
- 全文搜索路径表达式(了解)
- 谓语(条件筛选了解)
- 小结
- XML解析
- 综合案例
- 学习目标:
- 需求
- 案例效果
- 案例分析
- 自定义JDBC框架-代码实现
- Configuration
- 注解
- 映射类:Mapper
- SqlSession类
- 自定JDBC框架的使用
- 数据表
- 创建实体类
- UserMapper接口
- 配置文件:config.xml
- 测试类
1.反射
概念:反射技术就是对类进行解剖,解剖出"构造器"、"成员变量"、"成员方法"
- 构造器: 可以实例化对象
- 成员变量:可以赋值、取值
- 成员方法:调用方法
- 大白话:不使用new关键字,可以实例化对象,可以访问对象中的成员
反射技术,通常应用于:框架
反射技术的程序书写步骤:
- 获取Class对象
- 获取构造器
- 获取成员(成员方法、成员变量)
- 实例化对象 | 调用方法 | 给成员变量赋值取值
使用反射技术:
- 核心点:Class类
- Class类是什么呢?
- JVM中只能执行.class字节码文件(Java程序中会存在大量的.class文件)
- .class文件是通过
类加载器
读取到内存中,并基于这个.class文件创建出:Class对象- Class对象,就是指一个.class文件
- Class类的作用
- 通过Class对象,获取"构造器"、"成员方法"、"成员变量"
- Class类是什么呢?
步骤:
1、获取Class对象
Class cls = 类名.class;
Class cls = 对象名.getClass();
Class cls = Class.forName("类的全限定名");
//回顾:同步方法(线程), 非静态方法默认有一个同步锁:this
// 静态方法的同步锁:类名.class
2、基于Class对象,可以获取:构造器、成员方法、成员变量
- 构造器:Constructor类
Constructor c = Class对象.getConstructor();//无参构造器
Constructor c = Class对象.getConstructor(String.class);//有参构造器
- 成员方法:Method类
Method m = Class对象.getMethod("方法名");//获取指定方法名的无参方法
Method m = Class对象.getMethod("方法名",int.class);
- 成员变量:Field类
Field f = Class对象.getDeclaredField("属性名");
//针对私有成员:成员变量、成员方法、构造器,需要取消权限检查
f.setAccessible(true);//true就表示关闭本次权限检查
3、使用构造器,调用newInstance(...)
方法,来实例化对象
4、使用成员方法,调用invoke(...)
方法,来调用方法入栈执行
5、使用成员变量,调用set(...)
方法、get(...)
方法,对变量进行赋值、取值
2.注解
认知:
- 注解单独使用,没有任何意义。
- 通常注解会配合反射技术一起使用,常用于框架技术
注解的定义:
public @interface 注解名{
数据类型 属性名();
数据类型 属性名() default 默认值;
数据类型[] 属性名();
}
注解中的数据类型可以有哪些:
1. 8种基本数据类型
2. 枚举
3. String
4. Class
5. 注解
6. 以上所有类型的一维数组形式
注解的使用:
@注解名
public class 类{
@注解名(属性名=值)
private String 成员变量;
@注解名 //@Test
public void 成员方法(int 参数){
}
}
元注解:
- 作用:限定注解的使用位置、注解的生命周期
- @Target //指定注解的使用位置
- @Retention //设定注解的生命周期
- SOURCE : 只能在源码中使用 。 例:@Override
- CLASS :可以用于源码中、字节码文件中 【默认】
- RUNTIME : 可以用于源码、字节码文件、程序运行 例:@Test
注解解析(注解存在的意义)
注解解析的步骤:
1、获取一个对象(构造器Constructor、成员变量Field、成员方法Method)
2、判断对象上是否有指定的注解
有: 获取对象上的注解对象
对象上有注解后,获取注解对象中的属性值
//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;
}
3.动态代理
- 动态代理,提供了一个代理的对象,有了代理对象后,当访问某个方法时,会被代理对象拦截(拦截后可以对方法进行前增强、后增强【代理对象不会破坏原有方法的代码】)
- 动态代理的特点:
- 动态的创建.class文件(创建一个和被代理类实现相同父接口的子类[代理类])
- 动态的加载.class文件到内存中(创建Class对象)
- Proxy类需要一个"类加载器"
- 对程序员来讲,不需要书写代理类
- 动态代理的代码实现:
代理对象 = Proxy.newProxyInstance(类加载器 , 父接口 , 处理器)
类加载器: 动态的加载.class文件
父接口 : 代理类和被代理类需要拥有共同的父接口
处理器: 代理对象拦截了方法后,对方法进行前增强、后增强,是由处理器来书写逻辑
代理对象 = Proxy.newProxyInstance(
类.class.getClassLoader(), //类加载器
被代理类.class.getInterfaces(), //父接口
new InvocationHandler(){
public Object invoke(
Object 代理对象,
Method 被拦截的方法对象 ,
Object[] 方法中的实参
){
//业务逻辑
}
}
)
动态代理:
-
代理程序中的某个类中的功能,为该功能进行增强
动态代理实现的关键步骤:
-
被代理类(例:UserServiceImpl),必须有实现接口
-
创建被代理类对象(例: new UserServiceImpl() ),交给代理对象使用
动态代理的实现:
-
JDK已经提供了现成的代理对象的生成
-
Proxy类
-
静态方法:newProxyInstance(类加载器 , 接口数组 , 处理器)
-
-
Proxy创建一个子类
-
子类: 必须和被代理类实现相同的父接口
-
子类编译后是一个.class文件, 需要使用类加载器,加载.class文件到内存中
-
-
4.XML
1.XML概述
目标
- 什么是XML
什么是XML
-
英文:eXtensible Markup Language 可扩展的标记语言,由各种标记(标签,元素)组成。
-
可扩展:所有的标签都是自定义的,可以随意扩展的。如:<abc/>,<姓名>
-
标记语言:整个文档由各种标签组成。清晰,数据结构化!
-
XML是通用格式标准,全球所有的技术人员都知道这个东西,都会按照XML的规范存储数据,交互数据!!
XML作用
作用:总体上来说,就是为了存储维护数据的。
-
数据交换:不同的计算机语言之间,不同的操作系统之间,不同的数据库之间,进行数据交换。
-
配置文件:在后期我们主要用于各种框架的配置文件基本天天见。
- 比如 连接池:c3p0-config.xml 日志:logback.xml
小结
xml是什么?
- 可扩展的标记语言
- 标记:标签。例: <abc> </abc>
- xml文件是由N多个标签组成的
主要有哪两个作用?
- 存储数据
- 配置文件(主流)
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,"男");
效果
步骤
- 选择当前项目鼠标右键新建
- 新建一个File命名时,以 .xml结尾。这个文件就是xml文件
- 编写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>
- 通过浏览器解析XML的内容
-
注:XML以后通过Java来进行解析,很少直接在浏览器上显示。
小结:
-
xml文件,通常以.xml作为后缀名
-
xml文件中的首行必须书写: <?xml version="1.0" encoding="UTF-8" ?> (固定写法)
-
xml文件通常是由成对标签(开始标签、结束标签)组成
3.XML的组成:声明和元素
目标
-
XML文档中的声明
-
XML文档中的元素
XML组成
-
声明
-
元素(标签)
-
属性
-
注释
-
转义字符【实体字符】
-
CDATA 字符区
文档声明
<?xml version="1.0" encoding="utf-8" ?> 固定格式
- IDEA会自动提示。
- 文档声明必须为<?xml开头,以?>结束
- 文档声明必须从文档的1行1列位置开始,==必须在xml文档中的首行首列==
- 文档声明中常见的两个属性:
- version:指定XML文档版本。必须属性,这里一般选择1.0;
- encoding:指定当前文档的编码,可选属性,默认值是utf-8;
元素(标签、标记)
格式1: <person> 标签体 </person> 有标签体的标签 (双标签)
格式2: <person/> 没有标签体的标签 (空标签) (单标签)
- 元素是XML文档中最重要的组成部分;
- 普通元素的结构由开始标签、元素体、结束标签组成。【格式1】
- 元素体:元素体可以是元素,也可以是文本,例如:
<person>
标签中可以包含另一个标签
<name>张三</name>
</person>
- 空元素:空元素只有标签,而没有结束标签,但元素必须自己闭合,例如:
<sex/>
- 元素命名
- 可以保持与Java命名标识符一样的规则
- 区分大小写
- 不能使用空格,不能使用冒号
- 不建议以XML、xml、Xml开头
- 标签名不能数字开头,可以有数字
- 可以使用下划线
- 格式化良好的XML文档,有且仅有一个根元素。
- 错误演示:
- 元素没有结束
元素大写小写不一致
xml中多个根
小结
-
声明有哪两个常用的属性?
-
version
-
encoding
-
-
一个XML有几个根元素?
-
有且只能有一个根元素
-
-
XML标签命名不能有什么符号?
-
不能使用关键字xml、XML
-
不能有空格、不能有冒号
-
不能以数字作为开头
-
4.XML的组成:属性、注释和转义字符
属性的语法
<person id="110" id2="11">
-
属性是元素的一部分,它必须出现在元素的开始标签中
-
属性的定义格式:
属性名=“属性值”
,其中属性值必须使用单引或双引号括起来 -
一个元素可以有0~N个属性,但一个元素中不能出现同名属性
-
属性名不能使用空格 , 建议不要使用冒号等特殊字符,且必须以字母开头建议以Java的标识符定义规则做参考
<person id="123">
<name>张三</name>
</person>
注释
<!-- 注释内容 -->
<!--
注释内容 1
注释内容 2
-->
XML的注释与HTML相同,既以<!--
开始,-->
结束。不能嵌套。
Java中注释:
// 单行
/* */ 多行注释
/** */ 文档注释
XML注释:
<!-- 注释内容 -->
<!--<person>注释</person>--> <!-- 快捷键:Ctrl+/ :可以将整行进行注释-->
<person>三生三世</person> <!-- 快捷键:Ctrl+Shift+/:局部注释-->
转义字符[实体字符]
XML中的实体字符与HTML一样。因为很多符号已经被文档结构所使用,所以在元素体或属性值中想使用这些符号就必须使用实体字符
字符 | 预定义的转义字符 | 说明 |
---|---|---|
< | < | 小于(less than) |
> | > | 大于(greater than) |
" | " | 双引号(quotation) |
' | ' | 单引号(apostrophe) |
& | & | 和号(ampersand ) |
注意:严格地讲,在 XML 中仅有字符 "<"和"&" 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
转义字符应用示例:
假如您在 XML 文档中放置了一个类似 "<" 字符,那么这个文档会产生一个错误,这是因为解析器会把它解释为新元素的开始。因此你不能这样写:
<message>if salary < 1000 then </message>
为了避免此类错误,需要把字符 "<" 替换为实体引用,就像这样:
<message>if salary < 1000 then</message>
小结
- 属性必须出现在标签哪个位置?
- 和开始标签绑定在一起,书写在开始标签元素的后面
-
<person id="属性值"> </person>
- 同一个标签是否可以有同名的属性?
- 不可能。
- 允许有多个属性,属性之间使用空格分隔,但不能出现相同名称的属性
- 为什么要有转义字符(实体字符)?
- 在xml文件中,一些特定的符号已经被xml使用了。例:> & 等
- 希望在xml文档中,使用特定的符号,需要用:转义字符
- < =>
<
- & =>
&
- < =>
- 注释的快捷?
- ctrl + /
5.XML的组成:字符区(了解)
当大量的转义字符出现在xml文档中时,会使XML文档的可读性大幅度降低。这时如果使用CDATA段就会好一些。
CDATA (Character Data)字符数据区,格式如下:
<![CDATA[
文本数据 < > & ; " "
]]>
-
CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)
-
CDATA 部分由
<![CDATA[
开始,由]]>
结束;
例如:
<![CDATA[
if salary < 1000 then
]]
快捷模板:CD 回车
注意:
CDATA 部分不能包含字符串 "]]>"。也不允许嵌套的 CDATA 部分。
标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。
小结:
-
字符区的特点:
-
原样显示(书写的内容不会被xml解析器解析)
-
6.XML约束:DTD约束
目标
-
XML有哪两种约束
-
了解DTD约束
XML约束
在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
常见的xml约束:DTD、Schema
注意:我们对于约束的要求是能通过已写好的约束文件编写xml文档.
约束示例
效果说明:当编写xml文档时不符合指定dtd约束时,进行提示xml编写错误,如下图:
体验步骤:
步骤1:复制bookshelf.dtd文件
步骤2:bookshelf.dtd文件内容如下
<!ELEMENT 书架 (书+)> <!-- 声明了根元素,并指定了根元素下必须有的一个子元素 -->
<!-- 声明子元素 书 -->
<!ELEMENT 书 (书名,作者,售价)><!--约束元素书的子元素必须为书名、作者、售价-->
<!-- , 表示必须按照元素的顺序出现 -->
<!ELEMENT 书名 (#PCDATA)> <!-- #PCDATA : 表示文本内容 -->
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>
步骤三:新建books.xml,代码如下
引用 <!DOCTYPE 根元素 SYSTEM "dtd约束文件的路径">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 书架 SYSTEM "bookshelf.dtd"><!--指定使用bookshelf.dtd文件约束当前xml文档-->
<书架>
<书>
<书名>JavaWeb开发教程</书名>
<作者>张孝祥</作者>
<售价>100.00元</售价>
</书>
<书>
<书名>三国演义</书名>
<作者>罗贯中</作者>
<售价>100.00元</售价>
<测试>hello</测试><!--不符合约束,书的子元素必须为书名、作者、售价-->
</书>
</书架>
步骤四:idea开发工具books.xml的dtd约束验证不通过的效果如下
DTD学习要求
在企业实际开发中,我们很少自己编写DTD约束文档,通常情况下通过框架提供的DTD约束文档编写对应的XML文档。所以这一知识点的要求是可以根据DTD约束文档内容编写XML文档。
小结:
在xml文件中引入DTD约束文件
<!DOCTYPE 根元素 SYSTEM "约束文件的路径">
7.XML约束:Schema约束
目标
- 了解Schema约束
概念
- Schema 语言也也叫做 XSD(XML Schema Definition)。
- 其本身也是XML格式文档,但Schema文档扩展名为xsd,而不是xml。
- Schema 功能更强大,数据类型约束更完善。 比DTD强大,是DTD代替者。
约束示例
体验效果说明:体验schema约束XML文档中对元素体数据类型的约束。
效果如下:
DTD约束无法对具体数据类型进行约束,所以开发工具没有任何错误提示,如下效果:
实现步骤
步骤1:复制schema约束文件bookshelf.xsd,其中已对售价约束了数据类型,代码如下
<?xml version="1.0" encoding="UTF-8" ?>
<!--
传智播客schema教学实例文档.将注释中的以下内容复制到要编写的xml的声明下面
复制内容如下:
<书架 xmlns="http://www.itcast.cn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn bookshelf.xsd"
>
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.itcast.cn"
elementFormDefault="qualified">
<xs:element name='书架' >
<!-- complexType : 复杂元素
xml文档中的某个标签,有子标签或有属性,该标签就为:复杂元素
例: <书架> <书>
简单元素:
没有子标签、没有属性。 例:书名、作者、售价
-->
<xs:complexType>
<xs:sequence maxOccurs='unbounded' >
<xs:element name='书' >
<xs:complexType>
<xs:sequence>
<xs:element name='书名' type='xs:string' />
<xs:element name='作者' type='xs:string' />
<xs:element name='售价' type='xs:double' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
步骤2:新建books2.xml使用schema约束文件bookshelf.xsd,代码如下
<?xml version="1.0" encoding="UTF-8"?>
<书架
xmlns="http://www.itcast.cn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn bookshelf.xsd"
><!--指定schema文档约束当前XML文档-->
<书>
<书名>JavaScript网页开发</书名>
<作者>张孝祥</作者>
<售价>abc</售价>
</书>
</书架>
步骤3:开发工具提示效果
名称空间
一个XML文档最多可以使用一个DTD文件,但一个XML文档中使用多个Schema文件,若这些Schema文件中定义了相同名称的元素时,使用的时候就会出现名字冲突。这就像一个Java文件中使用了`import java.util.*`和`import java.sql.*`时,在使用Date类时,那么就不明确Date是哪个包下的Date了。
同理 , 在XML文档中就需要通过名称空间(namespace)来区分元素和属性是来源于哪个约束中的。
名称空间就在在根元素后面的内容 , 使用xmlns到引入约束 。
当一个XML文档中需要使用多个Schema文件的时候 , 有且仅有一个使用缺省的 , 其他的名称空间都需要起别名 。
参考资料中的 applicationContext.xml文件(spring框架的配置文件)
xmlns="http://www.itcast.cn"
<!-- 缺省的名称空间.使用此约束中的元素的时候只需要写元素名即可 例如:<书></书> -->
xmlns:aa="http://java.sun.com"
<!-- aa就是此约束的别名,使用此约束中的元素的时候就需要加上别名 例如:<aa:书></aa:书> -->
总之名称空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的名称空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。
schema学习要求
虽然schema功能比dtd强大,但是编写要比DTD复杂,同样以后我们在企业开发中也很少会自己编写schema文件。
xml编写与约束内容已经完成了,根据xml的作用我们了解到,无论是xml作为配置文件还是数据传输,我们的程序都要获取xml文档中的数据以便我们进行具体的业务操作,接下来我们就要学习XML解析技术Dom4j。
小结:
怎么引入xsd 文档
<?xml version="1.0" encoding="UTF-8" ?>
<根元素 xmlns="命名空间"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="命名空间 文件路径"
>
</根元素>
- schemaLocation:指定命名空间所在的文件
- 命名空间在约束文件的:targetNamespace中
5.Dom4j
1.XML解析
解析概述
- 当将数据存储在XML后,我们就希望通过程序获取XML的内容。我们使用Java基础所学的IO知识是可以完成的,不过需要非常繁琐的操作才可以完成,且开发中会遇到不同问题(只读、读写)。
- 人们为不同问题提供不同的解析方式,使用不同的解析器进行解析,方便开发人员操作XML。
解析方式和解析器
开发中比较常见的解析方式有三种,如下:
1. DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象
a)优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
b)缺点:XML文档过大,可能出现内存溢出
2. SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都触发对应的事件。
a)优点:处理速度快,可以处理大文件
b)缺点:只能读,逐行后将释放资源,解析操作繁琐。
3. PULL:Android内置的XML解析方式,类似SAX。(了解)
解析器,就是根据不同的解析方式提供具体实现。有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包
常见的解析器
小结
xml的常用解析方式:
1. DOM
2. SAX
3. PULL
基于解析方式,常用的解析工具包: Dom4j
2.Dom4j的基本使用
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/
通常我们会在项目中创建lib文件夹,将需要依赖的库放在这里。
库导入方式:
-
在IDEA中,选择项目鼠标右键--->弹出菜单-->open Module settings”-->Dependencies-->+-->JARs or directories... 找到dom4j-1.6.1.jar,成功添加之后点击"OK" 即可。
-
直接右键选择:Add as Library
小结
dom4j的解析思想,先把xml文档加载到内存中,从而得到一个DOM树,并创建一个Document对象去维护dom树。利用Document对象,就可以去解析DOM树(解析XML文件)
常用的方法
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的步骤:
-
创建SaxReader对象,调用read方法关联xml文件,得到一个Document对象
-
通过Document对象,获取根元素
-
获取根元素之后,就可以层层深剥,运用Element相关的API进行解析其子元素
方法演示
复制资料下的常用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值,以及书本名称,作者名称和价格.
步骤分析:
-
创建一个SaxReader对象,调用read方法加载一个xml文件获得文档对象
-
通过文档对象,获取根元素
-
通过根元素一层一层的进行解析子元素。
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>
步骤分析:
-
先创建一个Book类对应book元素
-
创建一个ArrayList集合用来存储解析后的book对象
-
创建SaxReader对象,调用read方法加载xml文件,得到文档对象
-
通过文档对象获取根元素,然后层层解析
代码实现:
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 + '\'' +
'}';
}
}
Dom4J结合XPath解析XML(了解)
介绍
XPath使用路径表达式来选取XML/HTML 文档中的元素节点或属性节点。节点是通过沿着路径 (path) 来选取的。XPath在解析XML/HTML文档方面提供了一独树一帜的路径思想。
XPath使用步骤
步骤1:导入jar包(dom4j和jaxen-1.1-beta-6.jar)
步骤2:通过dom4j的SaxReader获取Document对象
步骤3: 利用Xpath提供的api,结合xpath的语法完成选取XML文档元素节点进行解析操作。
Node接口中存在以下方法:
方法 | 作用 |
---|---|
List<Element> selectNodes("路径表达式") | 获取符合表达式的元素集合 |
Element selectSingleNode("路径表达式") | 获取符合表达式的唯一元素 |
我们熟知的Document,Element等都是Node的子类型,因此也能使用上述selectNode的方法。如下图
XPath语法(了解)
XPath也是一种解析XML或者HTML的方式。
-
XPath表达式,就是用于选取XML文档中节点的表达式字符串。获取XML文档节点元素一共有如下4种XPath语法方式:
-
绝对路径表达式方式 例如: /元素/子元素/子子元素...
-
相对路径表达式方式 例如: 子元素/子子元素.. 或者 ./子元素/子子元素..
-
全文搜索路径表达式方式 例如: //子元素//子子元素
-
谓语(条件筛选)方式 例如: //元素[@attr]
-
在xml解析得到的一个文档对象中,Document,Element,Attribute,Text ,都是接口Node的子类型。
Node中存在两个方法,可以结合xpath使用查询节点:
List<Node> selecteNodes(路径) :将所有满足路径的节点给获取出来
Node selecteSingleNode(路径) :获取单个满足路径的节点
xpath使用步骤:
-
创建SAXReader对象,调用read方法,关联xml文件。得到Document对象
-
就可以使用Document对象来调用方法传入xpath路径
绝对路径表达式(了解)
格式:
String xpath="/根元素/子元素/子子元素...";
绝对路径是以“/”开头,一级一级描述标签的层级路径就是绝对路径,这里注意不可以跨层级
演示需求(将素材中的Contact.xml拷贝到项目中)
采用绝对路径获取从根节点开始逐层的查找name节点列表并打印信息
String path="/contactList/contact/name";
实现步骤:
-
先创建SAXReader对象,调用read方法,将Contact.xml关联,得到Document对象
-
定义绝对路径
-
调用selectedNodes
public class Demo01 {
public static void main(String[] args) throws DocumentException {
//采用绝对路径获取从根节点开始逐层的查找name节点列表并打印信息
//1. 先创建SAXReader对象,调用read方法,将Contact.xml关联,得到Document对象
SAXReader sr = new SAXReader();
Document doc = sr.read("day15/xml/Contact.xml");
//2. 定义绝对路径
String xpath = "/contactList/contact/name";
//3. 调用selectedNodes
List<Node> nameNodes = doc.selectNodes(xpath);
for (Node nameNode : nameNodes) {
String text = nameNode.getText();
System.out.println("text = " + text);
}
}
}
text = 潘金莲
text = 武松
text = 武大狼
相对路径表达式(了解)
相对路径介绍
格式:
String xpath2="./子元素/子子元素"; // "./"代表当前元素路径位置
需求: 先采用绝对路径获取 contact 节点 再采用相对路径获取下一级name子节点并打印信息。
public class Demo01 {
public static void main(String[] args) throws DocumentException {
//先采用绝对路径获取 contact 节点 再采用相对路径获取下一级name子节点并打印信息。
//1. 先创建SAXReader对象,调用read方法,将Contact.xml关联,得到Document对象
SAXReader sr = new SAXReader();
Document doc = sr.read("day15/xml/Contact.xml");
//先使用绝对路径,获取contact
List<Node> contactNodes = doc.selectNodes("/contactList/contact");
for (Node contactNode : contactNodes) {
//使用相对路径获取name,并获取文本打印
Node node = contactNode.selectSingleNode("./name");
System.out.println("node.getText() = " + node.getText());
}
}
}
node.getText() = 潘金莲
node.getText() = 武松
node.getText() = 武大狼
全文搜索路径表达式(了解)
-
全文搜索路径介绍
格式:
String xpath1="//子元素//子子元素";
“/”符号,代表逐级写路径
“//”符号,不用逐级写路径,可以直接选取到对应的节点,是全文搜索匹配的不需要按照逐层级
举例 | 说明 |
---|---|
//contact | 找contact元素,无论元素在哪里 |
//contact/name | 找contact,无论在哪一级,但name一定是contact的子节点 |
//contact//name | contact无论在哪一种,name只要是contact的子孙元素都可以找到 |
//name |
需求:直接全文搜索所有的 name元素并打印
public class Demo01 {
public static void main(String[] args) throws DocumentException {
//需求:直接全文搜索所有的 name元素并打印
//1. 先创建SAXReader对象,调用read方法,将Contact.xml关联,得到Document对象
SAXReader sr = new SAXReader();
Document doc = sr.read("day15/xml/Contact.xml");
List<Node> nodes = doc.selectNodes("//name");
for (Node node : nodes) {
System.out.println("node.getText() = " + node.getText());
}
}
}
结论:
- / 表示根节点 (不能跨层级)
- // 表示全文任意节点 (可以跨层级)
谓语(条件筛选 了解)
介绍
谓语,又称为条件筛选方式,就是根据条件过滤判断进行选取节点
格式:
String xpath1="//元素[@属性名]";//查找元素对象,全文中只要含有该属性名的元素
String xpath2="//元素[@属性名=value]";//查找元素对象,全文中只要含有该属性,并指定的值
需求:查找含有id属性的contact元素
public class Demo01 {
public static void main(String[] args) throws DocumentException {
//查找含有id属性的contact元素
SAXReader sr = new SAXReader();
Document doc = sr.read("day15/xml/Contact.xml");
//List<Node> nodes = doc.selectNodes("//contact[@id]");//获取含有属性id的元素
List<Node> nodes = doc.selectNodes("//contact[@id=1]");//获取含有属性并且值为1的元素
for (Node node : nodes) {
//将节点,先转换Element
Element contact = (Element) node;
System.out.println("contact.elementText(\"name\") = " + contact.elementText("name"));
}
}
}
小结
在xpath解析xml方式中,最核心的就是使用了"路径表达式"
- xpath是利用"路径表达式"实现对xml文件的解析
路径表达式的语法:
- 绝对路径表达式
/根元素/子元素/子子元素/.... (一层一层查找)
- 相对路径表达式
./子元素/子子元素/.....
- 全文搜索路径表达式
//元素 (会跨层级进行全文查找)
- 条件筛选表达式
//元素[@属性名]
//元素[@属性名=属性值]
6.综合案例
学习目标:
-
理解所谓的框架是如何实现的,如何使用框架
需求
- 需求:自定义dao层jdbc框架
- 为了方便程序员操作数据库,让程序员更关注于sql代码层面和业务层面
案例效果
使用到的技术:
-
反射
-
注解
-
动态代理
-
xml解析:xpath
案例分析
自定义jdbc框架开发步骤:
1、通过软配置方式,和数据库连接
-
解析xml配置文件,获得:driver、url、username、password
-
C3P0连接池
-
根据配置文件中的参数,创建连接池对象
-
2、创建@Select注解
-
解析@Select注解中value值:
select查询语句
3、创建映射接口Mapper
-
把从@Select注解中解析出来的
select查询语句
,赋值给Mapper中的sql成员变量
4、创建SqlSession类
-
提供getMapper()方法,用来获取代理对象
-
说明:程序员在获取到代理对象后,利用代理对象调用某个方法时,会被代理对象拦截处理
-
自定义JDBC框架-代码实现
Configuration
-
作用:读取配置文件
-
配置文件名称被固定了:config.xml
-
/*配置类
1. 解析XML文件
2. 创建C3P0连接池
*/
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.01: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;
}
}
注解
@Select
//查询时使用的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String[] value();
}
@ResultType
//查询结果的封装类型
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResultType {
String value();
}
映射类:Mapper
-
作用:把通过解析@Select注解中的属性值(sql语句),存储到Mapper对象中
public class Mapper {
private String sql;//存储sql语句
public Mapper() {
}
public Mapper(String sql) {
this.sql = sql;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}
SqlSession类
-
作用:
-
解析@Select、@ResultType注解
-
和数据库交互
-
把数据结果封装到指定的对象中
-
@SuppressWarnings("all")
public class SqlSession {
/**
* 动态代理
*/ //clazz = UserMapper.class
//clazz = StudentMapper.class
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 *******/
//config.getInterName() 获取接口的全路径名称 例:com.itheima.UserMapper
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];//select * from user
//给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();
//通用型JDBC框架:考虑查询的结果类型是不同
//为了保障结果类型统一, 把所有的结果类型当作Class对象
Class cl = Class.forName(resultType);
//获取数据库连接池对象
DataSource ds = config.getDataSource();
//利用连接池对象,获取Connection对象
Connection conn = ds.getConnection();
//调用自定义方法: 执行sql查询语句
//参数1:Connection对象
//参数2:要执行的sql语句(select.....)
//参数3:select查询语句的结果要封装到指定的类型对象中
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 ...
//得到成员变量的名字
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;
}
}
自定JDBC框架的使用
数据表
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');
创建实体类
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 +
'}';
}
}
UserMapper接口
public interface UserMapper {
//编写SQL语句
//查询所有用户
@Select("select * from user")
@ResultType("com.itcast.pojo.User")
public abstract List queryAllUser();
}
配置文件: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>
测试类
public class Test1 {
@Test
public void testSelect() {
//使用jdbc框架中提供的API
//实例化
SqlSession sqlSession = new SqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.queryAllUser();
for (User u : users) {
System.out.println(u);
}
}
}