【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 地址查找名称。

下面的代码展示了作为示例的 TestInetAddressResolverProviderTestInetAddressResolverProvider 返回的是 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 机制进行注册。

SPI 注册

下面的代码进行测试。发送请求到 myserver.com。在不使用自定义解析器时,异常是 java.net.UnknownHostException: myserver.com,表示无法解析该主机名。在使用了自定义解析器之后,异常是 java.net.ConnectException: Connection refused: connect表示地址解析成功了。

版权所有 © 2024 灵动代码