【Java 18】网络地址解析 SPI
在 Java 中,java.net.InetAddress
用来进行主机名和 IP 地址之间的解析。在 Java 18 之前,这个解析过程的实现是固定的,由操作系统来负责完成解析,使用 hosts
文件和 DNS 服务器。
在 Java 18 中,新增了网络地址解析相关的 SPI,可以提供自定义的解析实现,从而绕开内置的解析方式。新增的 SPI 可以为很多场景提供支持。比如,支持新的解析协议,允许应用控制解析结果。尤其在进行测试时, 允许在代码中控制域名解析的结果,是非常实用的。
在日常的开发和测试中,经常会需要改变域名解析的结果。比如,把使用的第三方服务的域名,临时解析成本机地址,方便进行测试。通常的做法是修改 hosts
文件,来添加自定义的解析规则。hosts
文件的修改不太灵活,只能添加静态的规则。
网络地址解析 SPI 的出现,提供了足够的灵活性。该 SPI 的核心是抽象类 java.net.spi.InetAddressResolverProvider
。该抽象类有两个方法需要实现:
name
方法返回名称。get
方法返回作为解析器的InetAddressResolver
接口的对象。
get
方法的参数是 Configuration
接口的对象。Configuration
接口有两个方法:
builtinResolver
可以获取到系统内置的解析器。lookupLocalHostName
返回localhost
的名称。
作为解析器的 InetAddressResolver
接口则有两个方法:
lookupByName
方法根据名称查找 IP 地址。lookupByAddress
方法根据 IP 地址查找名称。
下面的代码展示了作为示例的 TestInetAddressResolverProvider
。TestInetAddressResolverProvider
返回的是 TestInetAddressResolver
对象。TestInetAddressResolver
把大部分请求都代理给系统内置的解析器。只有在名称是 myserver.com
时,返回 localhost
。从 Configuration
中得到系统内置的解析器对象之后,传递给 TestInetAddressResolver
。
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.spi.InetAddressResolver;
import java.net.spi.InetAddressResolverProvider;
import java.util.Objects;
import java.util.stream.Stream;
public class TestInetAddressResolverProvider extends
InetAddressResolverProvider {
@Override
public InetAddressResolver get(Configuration configuration) {
return new TestInetAddressResolver(configuration.builtinResolver());
}
@Override
public String name() {
return "test";
}
private record TestInetAddressResolver(
InetAddressResolver builtinResolver) implements
InetAddressResolver {
@Override
public Stream<InetAddress> lookupByName(String host,
LookupPolicy lookupPolicy) throws UnknownHostException {
if (Objects.equals(host, "myserver.com")) {
return Stream.of(InetAddress.getLocalHost());
}
return this.builtinResolver.lookupByName(host, lookupPolicy);
}
@Override
public String lookupByAddress(byte[] addr) throws UnknownHostException {
return this.builtinResolver.lookupByAddress(addr);
}
}
}
自定义解析器使用 Java 标准的 SPI 机制进行注册。
下面的代码进行测试。发送请求到 myserver.com
。在不使用自定义解析器时,异常是 java.net.UnknownHostException: myserver.com
,表示无法解析该主机名。在使用了自定义解析器之后,异常是 java.net.ConnectException: Connection refused: connect
表示地址解析成功了。