第一部分:Spring 的 AOP 技术------配置文件方式
1.1 AOP 相关术语
| 术语 | 说明 |
|---|---|
| Joinpoint(连接点) | 被拦截到的点。在 Spring 中,这些点指的是方法,因为 Spring 只支持方法类型的连接点 |
| Pointcut(切入点) | 要对哪些 Joinpoint 进行拦截的定义 |
| Advice(通知/增强) | 拦截到 Joinpoint 之后所要做的事情。分为前置通知、后置通知、异常通知、最终通知、环绕通知 |
| Target(目标对象) | 代理的目标对象 |
| Weaving(织入) | 把增强应用到目标对象来创建新的代理对象的过程 |
| Proxy(代理) | 一个类被 AOP 织入增强后,产生的结果代理类 |
| Aspect(切面) | 切入点和通知的结合 |
重点:切面 = 切入点表达式 + 通知(增强代码)。
1.2 AOP 配置文件方式入门
坐标依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- AOP 联盟 -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
引入 AOP 的 schema 约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
目标类配置:
<bean id="userService" class="com.qcbyjy.demo2.UserServiceImpl"/>
切面类:
package com.qcbyjy.demo2;
/**
* 自定义切面类 = 切入点(表达式)+ 通知(增强的代码)
*/
public class MyXmlAspect {
/**
* 通知
*/
public void log() {
// 发送手机短信
// 发送邮件/记录日志/事务管理
}
}
配置切面类与 AOP 增强:
<!-- 配置切面类 -->
<bean id="myXmlAspect" class="com.qcbyjy.demo2.MyXmlAspect"/>
<!-- 配置 AOP 的增强 -->
<aop:config>
<!-- 配置切面 = 切入点 + 通知组成 -->
<aop:aspect ref="myXmlAspect">
<!-- 前置通知:UserServiceImpl 的 save 方法执行前,会增强 -->
<aop:before method="log"
pointcut="execution(public void com.qcbyjy.demo2.UserServiceImpl.save())" />
</aop:aspect>
</aop:config>
重点:method 写通知方法名,pointcut 写切入点表达式。
1.3 切入点表达式
格式:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
| 部分 | 说明 |
|---|---|
| 修饰符 | 可以省略不写,不是必须出现 |
| 返回值类型 | 不能省略,可以使用 * 代替 |
| 包名 | com 不能省略,但可以用 * 代替;中间包名可以用 * 代替;想省略中间包名可用 .. |
| 类名 | 可以用 * 代替,如 *DaoImpl |
| 方法名 | 可以用 * 代替 |
| 参数 | 一个参数可用 * 代替;任意参数用 .. |
示例:
<aop:before method="log" pointcut="execution(* com.qcbyjy.*.*ServiceImpl.save*(..))" />
1.4 AOP 的通知类型
| 通知类型 | 说明 | 配置标签 |
|---|---|---|
| 前置通知 | 目标方法执行前进行增强 | <aop:before> |
| 最终通知 | 目标方法执行成功或失败都进行增强 | <aop:after> |
| 后置通知 | 目标方法执行成功后进行增强 | <aop:after-returning> |
| 异常通知 | 目标方法执行失败后进行增强 | <aop:after-throwing> |
| 环绕通知 | 目标方法执行前后都可以进行增强,目标对象的方法需要手动执行 | <aop:around> |
配置示例:
<aop:config>
<aop:aspect ref="myXmlAspect">
<!-- 前置通知 -->
<aop:before method="log" pointcut="execution(* com.qcbyjy.*.*ServiceImpl.save*(..))" />
<!-- 最终通知 -->
<aop:after method="log" pointcut="execution(* com.qcbyjy.*.*ServiceImpl.save*(..))" />
<!-- 后置通知 -->
<aop:after-returning method="log" pointcut="execution(* com.qcbyjy.*.*ServiceImpl.save*(..))" />
<!-- 异常通知 -->
<aop:after-throwing method="log" pointcut="execution(* com.qcbyjy.*.*ServiceImpl.save*(..))" />
<!-- 环绕通知 -->
<aop:around method="aroundLog" pointcut="execution(* com.qcbyjy.*.*ServiceImpl.save*(..))" />
</aop:aspect>
</aop:config>
重点:
- 最终通知不管成功失败都会执行。
- 后置通知仅在成功后执行。
- 环绕通知需要手动执行目标对象的方法。
易错点:后置通知与最终通知混淆。后置只在成功时执行,最终无论成败都执行。
第二部分:Spring 的 AOP 技术------注解方式
2.1 AOP 注解方式入门
切面类:
package com.qcbyjy.demo3;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 切面类
*/
@Component // 把该类交给 IOC 去管理
@Aspect // 声明是切面类 == <aop:aspect ref="myXmlAspect">
public class MyAnnoAspect {
/**
* 通知的方法
*/
// @Before(value = "切入点的表达式")
@Before(value = "execution(public * com.qcbyjy.demo3.OrderServiceImpl.save(..))")
public void log() {
System.out.println("增强了...");
}
}
配置文件中开启自动代理:
<aop:aspectj-autoproxy/>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_demo3.xml")
public class Demo3 {
@Autowired
private OrderService orderService;
@Test
public void run1() {
orderService.save();
}
}
重点:@Aspect 声明切面类,@Component 交给 IOC 管理,@Before 等注解声明通知类型与切入点。
2.2 通知类型的注解
| 注解 | 说明 |
|---|---|
@Before |
前置通知 |
@AfterReturning |
后置通知 |
@Around |
环绕通知(目标对象方法默认不执行,需要手动执行) |
@After |
最终通知 |
@AfterThrowing |
异常抛出通知 |
2.3 纯注解方式
package com.qcbyjy.demo3;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration // 配置类
@ComponentScan(basePackages = "com.qcbyjy.demo3")
@EnableAspectJAutoProxy // 开启 AOP 自动代理
public class SpringConfig {
}
重点:@EnableAspectJAutoProxy 等价于 XML 中的 <aop:aspectj-autoproxy/>。
易错点:注解方式下忘记加 @EnableAspectJAutoProxy 或 XML 中忘记加 <aop:aspectj-autoproxy/>,会导致切面不生效。
第三部分:Spring 框架的 JDBC 模板技术
3.1 JDBC 模板技术概述
Spring 框架提供了很多持久层的模板类来简化编程,使用模板类编写程序会变得简单。模板类命名规律:XxxTemplate。
提供的 JDBC 模板:JdbcTemplate。它封装了 Connection、Statement、ResultSet 等操作,并管理事务。
3.2 JDBC 模板类的使用(自己 new 对象方式)
坐标依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
测试代码(自己 new 对象方式):
@Test
public void run1() {
// 创建连接池对象,Spring 框架内置了连接池对象
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// 设置 4 个参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 提供模板,创建对象
JdbcTemplate template = new JdbcTemplate(dataSource);
// 完成数据的增删改查
template.update("insert into account values (null,?,?)", "熊大", 1000);
}
重点:DriverManagerDataSource 是 Spring 内置连接池,JdbcTemplate 需要传入 DataSource。
3.3 使用 Spring 框架管理模板类
<!-- 配置连接池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///spring_db" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- 配置 jdbc 模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
3.4 Spring 框架管理开源连接池(Druid)
坐标:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
属性文件 jdbc.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=root
加载属性文件两种写法:
<!-- 第一种:PropertyPlaceholderConfigurer -->
<bean id="placeholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"/>
</bean>
<!-- 第二种:使用 context 标签 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
配置数据源与模板:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
重点:使用 ${key} 读取属性文件中的值;<context:property-placeholder> 比 PropertyPlaceholderConfigurer 更简洁。
3.5 JDBC 模板的简单操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo1_1 {
@Autowired
private JdbcTemplate jdbcTemplate;
// 增加
@Test
public void run1() {
jdbcTemplate.update("insert into account values (null,?,?)", "熊四", 800);
}
// 修改
@Test
public void run2() {
jdbcTemplate.update("update account set name = ?,money = ? where id = ?", "光头强", 100, 7);
}
// 删除
@Test
public void run3() {
jdbcTemplate.update("delete from account where id = ?", 7);
}
// 通过 id 查询
@Test
public void run4() {
Account account = jdbcTemplate.queryForObject(
"select * from account where id = ?", new BeanMapper(), 6);
System.out.println(account);
}
// 查询所有
@Test
public void run5() {
List<Account> list = jdbcTemplate.query("select * from account", new BeanMapper());
for (Account account : list) {
System.out.println(account);
}
}
}
BeanMapper 实现类:
class BeanMapper implements RowMapper<Account> {
/**
* 一行一行进行数据封装
*/
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getDouble("money"));
return account;
}
}
重点:
- 增删改都用
update。 - 查询单个对象用
queryForObject。 - 查询列表用
query。 - 结果集封装用
RowMapper实现类。
易错点:查询单个对象时,若结果为空或结果多于一条,queryForObject 会抛异常。
第四部分:模拟转账开发
4.1 Service 代码
public interface AccountService {
/**
* 转账的方法
*/
public void pay(String out, String in, double money);
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void pay(String out, String in, double money) {
// 调用 dao 方法
accountDao.outMoney(out, money);
accountDao.inMoney(in, money);
}
}
4.2 Dao 代码(第一种方式:直接注入 JdbcTemplate)
接口:
public interface AccountDao {
public void outMoney(String out, double money);
public void inMoney(String in, double money);
}
实现类:
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void outMoney(String out, double money) {
jdbcTemplate.update("update account set money = money - ? where name = ?", money, out);
}
@Override
public void inMoney(String in, double money) {
jdbcTemplate.update("update account set money = money + ? where name = ?", money, in);
}
}
配置:
<bean id="accountService" class="com.qcbyjy.demo2.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean id="accountDao" class="com.qcbyjy.demo2.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
4.3 Dao 代码(第二种方式:继承 JdbcDaoSupport)
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void outMoney(String out, double money) {
this.getJdbcTemplate().update(
"update account set money = money - ? where name = ?", money, out);
}
@Override
public void inMoney(String in, double money) {
this.getJdbcTemplate().update(
"update account set money = money + ? where name = ?", money, in);
}
}
配置:
<bean id="accountDao" class="com.qcbyjy.demo3.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
重点:
- 继承
JdbcDaoSupport后,无需自己声明JdbcTemplate,直接通过getJdbcTemplate()获取。 - 此时注入的是
dataSource,而不是jdbcTemplate。
易错点:两种 Dao 写法注入的属性不同。第一种注入 jdbcTemplate,第二种注入 dataSource,不要混淆。
4.4 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_dao1.xml")
public class Demo2 {
@Autowired
private AccountService accountService;
@Test
public void testPay() {
accountService.pay("熊大", "熊二", 100);
}
}
第五部分:XML 基础
5.1 XML 概述
XML 全称为 Extensible Markup Language,即可扩展的标记语言,是 SGML 的子集。语法上和 HTML 相似,但 HTML 元素固定,XML 标签可由用户自定义。
W3C 于 1998 年 2 月发布 1.0 版本;2004 年 2 月发布 1.1 版本,但 1.1 不向下兼容 1.0,所以没人用。学习的是 1.0 版本。
5.2 XML 的作用
| 作用 | 说明 |
|---|---|
| 程序的配置文件 | 最常见用途 |
| 数据交换 | 不同语言之间交换数据 |
| 小型数据库 | 用来当数据库存储数据 |
5.3 XML 与 HTML、Properties 比较
| 对比项 | XML | HTML | Properties |
|---|---|---|---|
| 元素 | 可自定义 | 固定 | 无 |
| 解析 | 通常需自己写解析器 | 浏览器解析 | Properties 类 |
| 能力 | 可做配置文件、数据交换、小型数据库 | 只能表示网页 | 只能存储平面信息 |
重点:属性文件只能存储平面信息,XML 可存储结构化信息。
5.4 XML 文档声明
最简单的声明:
<?xml version="1.0"?>
| 属性 | 说明 |
|---|---|
version |
说明当前 XML 文档版本,必须写 1.0 |
encoding |
说明字符编码集,可选,默认 UTF-8 |
standalone |
说明是否独立文档,yes 表示独立,no 表示依赖外部约束文件,默认 yes |
重点:
- XML 区分大小写,这点与 HTML 不同。
- 没有 XML 文档声明的 XML 文档,不是格式良好的 XML 文档。
- XML 文档声明必须从文档第 1 行第 1 列开始。
- 若文档实际编码是
gb2312,而encoding写UTF-8,一定出错。
5.5 XML 元素
元素格式:
- 开始标签、元素体(内容)、结束标签,如
<hello>大家好</hello>。 - 空元素只有开始标签,没有元素体和结束标签,但一定要闭合,如
<hello/>、<hr/>。 - 元素可以包含子元素或文本数据。
- 元素可以嵌套,但必须合法嵌套。
根元素:
- 格式良好的 XML 文档必须且仅有一个根元素。
元素命名规范:
| 规范 | 说明 |
|---|---|
| 区分大小写 | <a> 和 <A> 是两个元素 |
| 不能以数字开头 | <1a> 错误 |
| 最好不要以 xml 开头 | <xml>、<Xml>、<XML> |
| 不能包含空格 | --- |
元素属性:
- 属性由属性名与属性值构成,中间用等号连接。
- 属性值必须使用引号括起来,单引或双引。
- 属性必须定义在元素的开始标签中。
- 一个元素中不能包含相同的属性名。
元素中的空白:
- 换行和缩进都是空白,用于增强可读性。
- XML 解析器可能因为空白出现错误解读,编写解析程序时要小心。
5.6 注释
<!-- 注释内容 -->
重点:注释以 <!-- 开头,以 --> 结束;注释中不能包含 --。
5.7 转义字符和 CDATA 区
在 XML 文档中,有些字符是特殊的,不能直接作为文本数据,需要使用转义字符表示。
| 字符 | 预定义实体引用 |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
& |
& |
重点:转义字符以 & 开头,以 ; 结束。
CDATA 区:
<a><![CDATA[<a>]]></a>
重点:
- 在 CDATA 段中出现的
<、>、"、'、&都无需使用转义字符。 - CDATA 段中不能包含
]]>,即 CDATA 段的结束定界符。
5.8 XML 约束
XML 文档约束是用另一个文件来约束 XML 文档的结构。
| 约束类型 | 特点 |
|---|---|
| DTD | 平面式文档,不是 XML 文档,扩展名 .dtd,最早的 XML 约束 |
| Schema | 本身也是 XML 文档,扩展名 .xsd,比 DTD 更强大,用来替代 DTD |
重点:会读即可。
DTD 示例:
<!ELEMENT students (student+)>
<!ELEMENT student (name,age,sex)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
解读:
students元素中可以包含 1 到 n 个student元素。student元素必须包含name、age、sex元素,顺序固定。name、age、sex元素内容为文本。
Schema 与 DTD 对比:
| 对比项 | DTD | Schema |
|---|---|---|
| 语法 | 不遵守 XML 语法 | 基于 XML 语法 |
| 数据类型 | 有限,与数据库不一致 | 大大扩充,可自定义 |
| 可扩展性 | 不可扩展 | 支持元素继承、属性组 |
| 命名空间 | 不支持 | 支持 |
5.9 XML 解析技术
XML 解析方式分为两种:
| 方式 | 说明 |
|---|---|
| DOM | Document Object Model,W3C 组织提供的解析 XML 文档的标准接口 |
| SAX | Simple API for XML,社区讨论的产物,是一种事实上的标准 |
重点:DOM 和 SAX 不是针对 Java 语言的解析技术,而是跨语言的解析方式。
5.10 DOM4J
DOM4J 是针对 Java 开发人员专门提供的 XML 文档解析规范,不同于 DOM,但与 DOM 相似。对 Java 开发人员来说,使用 DOM4J 比使用 DOM 更加方便。
DOM4J 对 DOM 和 SAX 提供了支持,解析结果是 org.dom4j.Document,而不是 org.w3c.Document。
DOM4J 类结构:
- 所有 XML 组成部分都是一个
Node。 Branch表示可以包含子节点的节点,Document和Element都是Branch的子接口。Attribute是属性节点。CharacterData是文本节点,有三个子接口:CDATA、Text、Comment。
获取 Document 对象:
SAXReader reader = new SAXReader();
reader.setValidation(true);
Document doc = reader.read("src/students.xml");
保存 Document 对象:
doc.addDocType("students", "", "students.dtd");
OutputFormat format = new OutputFormat("\t", true);
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileWriter(xmlName), format);
writer.write(doc);
writer.close();
创建 Document 对象:
Document doc = DocumentHelper.createDocument();
重点:
- 加载 XML 文档用
SAXReader.read()。 - 保存 XML 文档用
XMLWriter.write()。 - 创建 Document 用
DocumentHelper.createDocument()。
5.11 Document 操作
常用方法:
| 方法 | 说明 |
|---|---|
Element getRootElement() |
Document 的方法,获取根元素 |
List elements() |
Element 的方法,获取所有孩子元素 |
String attributeValue(String name) |
Element 的方法,获取指定名字的属性值 |
Element element(String name) |
Element 的方法,获取第一个指定名字的子元素 |
String elementText(String name) |
Element 的方法,获取第一个指定名字的子元素的文本内容 |
遍历步骤:
获取 Document 对象 → 获取 root 元素 → 获取 root 所有子元素 → 遍历每个 student 元素
→ 打印 student 元素 number 属性 → 打印 name、age、sex 子元素内容
5.12 XPath
XPath 即 XML 路径语言(XML Path Language),是一种用来确定 XML 文档中某部分位置的语言。基于 XML 的树状结构,提供在数据结构树中找寻节点的能力。
DOM4J 中 Node 接口常用的三个方法:
| 方法 | 说明 |
|---|---|
List selectNodes(String xpathExpression) |
在当前节点中查找满足 XPath 表达式的所有子节点 |
Node selectSingleNode(String xpathExpression) |
在当前节点中查找满足 XPath 表达式的第一个子节点 |
String valueOf(String xpathExpression) |
在当前节点中查找满足 XPath 表达式的第一个子节点的文本内容 |
XPath 示例:
Document doc = reader.read(new FileInputStream("person.xml"));
Element root = doc.getRootElement();
List<Element> elist = root.selectNodes("/persons");
List<Element> elist1 = root.selectNodes("/persons/person/name");
List<Element> elist2 = root.selectNodes("//age");
List<Element> elist3 = root.selectNodes("//person/age");
List<Element> elist4 = root.selectNodes("//address");
List<Element> elist5 = root.selectNodes("//");
List<Element> elist6 = root.selectNodes("/persons/person[1]");
List<Element> elist7 = root.selectNodes("/persons/person[last()]");
List<Element> elist8 = root.selectNodes("//@id");
List<Element> elist9 = root.selectNodes("//person[@id]");
List<Element> elist10 = root.selectNodes("//person[@*]");
List<Element> elist11 = root.selectNodes("//*[not(@*)]");
List<Element> elist12 = root.selectNodes("//person[@id='person1']");
重点:
/表示从根节点开始。//表示从任意位置开始查找。[1]表示第一个,[last()]表示最后一个。@id表示属性。[@id]表示具有id属性的节点。[@*]表示具有任意属性的节点。