1. 概述
在本文中,我们将演示如何使用 JDK 提供的 URL
和 URI
类来解析 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 类解析
相比 URL
,java.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 提取参数
有时候你只需要提取某一个关键参数(比如 token
或 user
),而不想引入完整的解析逻辑。这时正则表达式是一种简洁高效的方案。
示例代码如下:
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