1. 概述

在本文中,我们将演示如何使用 JDK 提供的 URLURI 类来解析 URL 字符串,并进一步介绍如何通过正则表达式(Regex)提取 URL 中的参数。

这类操作在实际开发中非常常见,比如处理回调地址、解析前端传参或对接第三方服务。但由于 URL 包含大量特殊字符(如 ?, &, =, % 等),直接手动解析容易踩坑 ❌。因此,合理利用标准库工具类至关重要 ✅。

我们将从三种方式展开:

  • 使用 java.net.URL 类进行结构化解析
  • 使用更通用的 java.net.URI
  • 借助正则表达式快速提取特定参数

2. 使用 JDK 的 URL 类解析

java.net.URL 是 Java 标准库中用于表示统一资源定位符的类,能自动解析协议、主机、端口、查询参数等部分。

为了方便提取查询参数(query parameters),我们可以为 URL 类扩展一个便捷方法:

private fun URL.findParameterValue(parameterName: String): String? {
    return query.split('&').map {
        val parts = it.split('=')
        val name = parts.firstOrNull() ?: ""
        val value = parts.drop(1).firstOrNull() ?: ""
        Pair(name, value)
    }.firstOrNull { it.first == parameterName }?.second
}

📌 说明

  • query 是 URL 中 ? 后的部分,例如 reqNo=3&user=Bob&age=12
  • 我们将其按 & 分割,再对每个键值对用 = 拆分
  • 构建 Pair(name, value) 并查找匹配参数名的那一项
  • ⚠️ 注意:此实现未做 URL 解码(如 %E4%B8%AD → 中文),也未处理重复参数,仅作示例用途

接下来验证功能是否正常:

@Test
fun `given url when parsed with URL class returns user parameter`() {
    val url = URL(urlToParse)
    val userNameFromUrl = url.findParameterValue("user")
    assertThat(userNameFromUrl).isEqualTo("Bob")
    assertThat(url.protocol).isEqualTo("https")
    assertThat(url.host).isEqualTo("www.baeldung.com")
}

✅ 测试通过,不仅能正确提取 user=Bob,还能访问 .protocol.host 等结构化字段。

💡 小贴士:URL 类依赖网络协议处理器,如果协议不支持(比如自定义 scheme),可能会抛出异常。生产环境建议优先考虑 URI

3. 使用 URI 类解析

相比 URLjava.net.URI 更偏向于“标识”而非“定位”,不涉及网络连接逻辑,更适合纯粹的字符串解析场景。

我们同样可以为其编写扩展函数:

private fun URI.findParameterValue(parameterName: String): String? {
    return rawQuery.split('&').map {
        val parts = it.split('=')
        val name = parts.firstOrNull() ?: ""
        val value = parts.drop(1).firstOrNull() ?: ""
        Pair(name, value)
    }.firstOrNull { it.first == parameterName }?.second
}

区别在于:

  • 使用 rawQuery 而非 query:前者返回原始编码字符串,后者会尝试解码
  • URI 的属性名为 scheme 而非 protocol

测试代码如下:

@Test
fun `given url when parsed with URI class returns user parameter`() {
    val uri = URI(urlToParse)
    val userNameFromUrl = uri.findParameterValue("user")
    assertThat(userNameFromUrl).isEqualTo("Bob")
    assertThat(uri.scheme).isEqualTo("https")
    assertThat(uri.host).isEqualTo("www.baeldung.com")
}

✅ 结果一致,且 URI 更轻量、更安全,推荐在非网络操作场景下使用。

📌 推荐使用场景:

  • 参数解析、路由匹配 ✅
  • 构造 RESTful 接口路径 ✅
  • 需要处理国际化域名或复杂编码时 ✅

4. 使用 Regex 提取参数

有时候你只需要提取某一个关键参数(比如 tokenuser),而不想引入完整的解析逻辑。这时正则表达式是一种简洁高效的方案。

示例代码如下:

class ParseUrlTest {

    companion object {
        private const val urlToParse = "https://www.baeldung.com/?reqNo=3&user=Bob&age=12"
        private val REGEX_PATTERN = """https://www.baeldung.com/?.*[?&]user=([^#&]+).*""".toRegex()
    }

    @Test
    fun `given url when parsed should return user parameter`() {
        val userNameFromUrl = REGEX_PATTERN.matchEntire(urlToParse)?.groups?.get(1)?.value
        assertThat(userNameFromUrl).isEqualTo("Bob")
    }
}

🔍 正则解释:

  • https://www.baeldung.com/?:基础域名,/? 表示可选斜杠
  • .*[?&]user=:前面可能有其他参数,用 [?&] 匹配参数分隔符
  • ([^#&]+):捕获组,匹配直到遇到 &# 或结尾为止的值
  • .*:后续内容忽略

⚠️ 注意事项:

  • matchEntire() 要求整个字符串匹配,若只想找子串可用 find()
  • 第一个捕获组索引为 1(0 是整条匹配)
  • ❌ 不适合复杂场景,如多值参数、嵌套编码、动态 host 等

✅ 适用场景举例:

  • 日志中快速提取 token
  • 单元测试中验证重定向 URL 是否包含预期参数
  • 前端 mock 数据生成

5. 总结

方法 优点 缺点 推荐场景
URL 结构清晰,API 直观 依赖协议处理器,可能抛异常 需要发起真实请求时
URI 轻量、安全、通用性强 需手动处理编码 绝大多数解析场景 ✅
Regex 快速、灵活 易写错,难维护 提取单一固定参数

📌 最佳实践建议:

  • ✅ 优先使用 URI + 手动拆分 query 参数
  • ✅ 若项目中频繁处理 URL,建议封装成工具类或使用 Apache Commons Lang 中的 URIBuilder
  • ❌ 避免手撕复杂正则,尤其是涉及编码、多重参数时

🔗 示例源码已托管至 GitHub:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-6


原始标题:Parsing a URL String