Spring 框架AOP与 XML 基础

第一部分: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 文档中,有些字符是特殊的,不能直接作为文本数据,需要使用转义字符表示。

字符 预定义实体引用
< &lt;
> &gt;
" &quot;
' '
& &amp;

重点:转义字符以 & 开头,以 ; 结束。

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 属性的节点。
  • [@*] 表示具有任意属性的节点。
相关推荐
m0_639906941 小时前
SpringBoot 实现大模型统一调度网关|对接 OpenAI、Claude 协议差异与适配方案
java·spring boot·spring
雪芽蓝域zzs1 小时前
第 1 章:IDEA 全局配置 Maven + 新建 SpringBoot3 Maven 项目
java·maven·intellij-idea
geovindu1 小时前
rust:N-Barrier Pattern
开发语言·后端·设计模式·rust·屏障模式·并发型模式
AI宇宙狂想家1 小时前
开源 AI 视频工具进化史:从难用到好用,OpenMontage 改了什么
开发语言·前端·人工智能·开源·ai创业·openmontage
代码什么用1 小时前
Maven&Nexus
java·maven
顧棟1 小时前
OpenAPI 网关:鉴权 + 流量治理 一体化设计方案与实现
java·spring boot·设计模式
Rain的Java大神之路1 小时前
「设计一个在线文档协同编辑系统」:从 WebSocket、OT/CRDT 到快照回放,一篇讲透这道面试顶流题
java·后端·面试
大鹏说大话2 小时前
和小鹅通相近的知识付费小程序平台有哪些
开发语言