1. 概述
本文将深入探讨Java网络编程中的底层操作,重点聚焦URL的处理。URL是网络资源的地址或引用,Java代码可通过java.net.URL
类表示这些资源地址。
Java平台内置网络支持,封装在java.net
包中:
import java.net.*;
2. 创建URL
从Java 20开始,java.net.URL
的所有构造方法已被弃用,推荐使用java.net.URI
构造URL。
通过URI.toURL()
创建URL对象:
URL home = new URI("http://baeldung.com/a-guide-to-java-sockets").toURL();
这创建了一个绝对URL对象,包含访问资源的完整信息。
也可以创建相对URI对象,但需注意:toURL()
方法禁止将非绝对URI转换为URL:
@Test
public void givenRelativeUrl_whenCreatesRelativeUrl_thenThrows() {
URI uri = new URI("/a-guide-to-java-sockets");
Assert.assertThrows(IllegalArgumentException.class, () -> uri.toURL());
}
其他创建方式需先了解URL组件(见下节)。
3. URL组件剖析
URL由多个组件构成,本节逐一解析。
3.1 协议标识符
协议与资源名通过://
分隔。例如http://baeldung.com
中:
http
是协议标识符baeldung.com
是资源名
获取协议:
@Test
public void givenUrl_whenCanIdentifyProtocol_thenCorrect(){
URL url = new URI("http://baeldung.com").toURL();
assertEquals("http", url.getProtocol());
}
3.2 端口
获取端口信息:
@Test
public void givenUrl_whenGetsDefaultPort_thenCorrect(){
URL url = new URI("http://baeldung.com").toURL();
assertEquals(-1, url.getPort()); // 未显式指定返回-1
assertEquals(80, url.getDefaultPort()); // HTTP默认端口80
}
显式指定端口示例:
@Test
public void givenUrl_whenGetsPort_thenCorrect(){
URL url = new URI("http://baeldung.com:8090").toURL();
assertEquals(8090, url.getPort());
}
3.3 主机名
主机名是://
后到域名扩展前的部分(如.com
):
@Test
public void givenUrl_whenCanGetHost_thenCorrect(){
URL url = new URI("http://baeldung.com").toURL();
assertEquals("baeldung.com", url.getHost());
}
3.4 文件名
主机名后的部分统称文件名,包含路径和查询参数:
@Test
public void givenUrl_whenCanGetFileName_thenCorrect1() {
URL url = new URI("http://baeldung.com/guidelines.txt").toURL();
assertEquals("/guidelines.txt", url.getFile());
}
@Test
public void givenUrl_whenCanGetFileName_thenCorrect2() {
URL url = new URI("http://baeldung.com/articles?topic=java&version=8").toURL();
assertEquals("/articles?topic=java&version=8", url.getFile());
}
3.5 路径参数
仅获取路径部分(不含查询参数):
@Test
public void givenUrl_whenCanGetPathParams_thenCorrect() {
URL url = new URI("http://baeldung.com/articles?topic=java&version=8").toURL();
assertEquals("/articles", url.getPath());
}
3.6 查询参数
获取查询字符串:
@Test
public void givenUrl_whenCanGetQueryParams_thenCorrect() {
URL url = new URI("http://baeldung.com/articles?topic=java&version=8").toURL();
assertEquals("topic=java&version=8", url.getQuery());
}
4. 通过组件构建URL
掌握URL组件后,可通过组件部分构建URL对象。
4.1 使用URI构造器
通过协议、主机、文件名和片段构建:
@Test
public void givenUrlComponents_whenConstructsCompleteUrl_thenCorrect() {
String protocol = "http";
String host = "baeldung.com";
String file = "/guidelines.txt";
String fragment = "myImage";
URL url = new URI(protocol, host, file, fragment).toURL();
assertEquals("http://baeldung.com/guidelines.txt#myImage", url.toString());
}
包含用户信息、端口和查询参数的完整构造:
@Test
public void givenUrlComponents_whenConstructsCompleteUrl_thenCorrect2() {
String protocol = "http";
String username = "admin";
String host = "baeldung.com";
String file = "/articles";
String query = "topic=java&version=8";
String fragment = "myImage";
URL url = new URI(protocol, username, host, -1, file, query, fragment).toURL();
assertEquals("http://admin@baeldung.com/articles?topic=java&version=8#myImage", url.toString());
}
4.2 使用Apache HttpClient的URIBuilder
当URL含多个查询参数时,手动构建易出错。推荐使用URIBuilder(需添加依赖):
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
链式调用构建URL:
@Test
public void givenUrlParameters_whenBuildUrlWithURIBuilder_thenSuccess() throws URISyntaxException, MalformedURLException {
URIBuilder uriBuilder = new URIBuilder("http://baeldung.com/articles");
uriBuilder.setPort(9090);
uriBuilder.addParameter("topic", "java");
uriBuilder.addParameter("version", "8");
URL url = uriBuilder.build().toURL();
assertEquals("http://baeldung.com:9090/articles?topic=java&version=8", url.toString());
}
批量添加参数(从Map转换):
@Test
public void givenUrlParametersInMap_whenBuildUrlWithURIBuilder_thenSuccess()
throws URISyntaxException, MalformedURLException {
Map<String, String> paramMap = ImmutableMap.of("topic", "java", "version", "8");
URIBuilder uriBuilder = new URIBuilder("http://baeldung.com/articles");
uriBuilder.setPort(9090);
uriBuilder.addParameters(paramMap.entrySet()
.stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(toList()));
URL url = uriBuilder.build().toURL();
assertEquals("http://baeldung.com:9090/articles?topic=java&version=8", url.toString());
}
4.3 使用Spring-web的UriComponentsBuilder
Spring框架提供UriComponentsBuilder(需依赖):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.8</version>
</dependency>
构建示例:
@Test
public void givenUrlParameters_whenBuildUrlWithSpringUriComponentsBuilder_thenSuccess()
throws MalformedURLException {
URL url = UriComponentsBuilder.newInstance()
.scheme("http")
.host("baeldung.com")
.port(9090)
.path("articles")
.queryParam("topic", "java")
.queryParam("version", "8")
.build()
.toUri()
.toURL();
assertEquals("http://baeldung.com:9090/articles?topic=java&version=8", url.toString());
}
5. 总结
本文系统讲解了Java中URL类的使用,包括:
- ✅ URL对象创建方式
- ✅ 核心组件解析(协议/端口/主机/路径/查询参数)
- ✅ 多种构建URL的实践方案(标准API/Apache HttpClient/Spring)
⚠️ 踩坑提示:Java 20后URL构造器已弃用,务必通过URI创建。复杂URL建议使用Apache或Spring的构建器,避免手动拼接错误。
完整代码示例见GitHub仓库。