static { // create the impl impl = InetAddressImplFactory.create(); // get name service if provided and requested String provider = null;; String propPrefix = "sun.net.spi.nameservice.provider."; int n = 1; nameServices = new ArrayList<NameService>(); provider = AccessController.doPrivileged( new GetPropertyAction(propPrefix + n)); while (provider != null) { NameService ns = createNSProvider(provider); if (ns != null) nameServices.add(ns); n++; provider = AccessController.doPrivileged( new GetPropertyAction(propPrefix + n)); } // if not designate any name services provider, // create a default one if (nameServices.size() == 0) { NameService ns = createNSProvider("default"); nameServices.add(ns); } }
因为是通过InetAddress的static块初始化的,所以必须在使用InetAddress之前配置sun.net.spi.nameservice.provider.<n>=<default|dns,sun|...>配置项。否则不会生效。具体可以参见SO上的这篇帖子:Clean DNS server in JVM。
另外需要注意的是在JDK7之前,只有第一个成功加载的nameservice参与解析。其他的nameservice并不起作用。
sun.net.spi.nameservice.provider.<n>=<default|dns,sun|...>
Specifies the name service provider that you can use. By default, Java will use the system configured name lookup mechanism, such as file, nis, etc. You can specify your own by setting this option. takes the value of a positive number, it indicates the precedence order with a small number takes higher precendence over a bigger number. Aside from the default provider, the JDK includes a DNS provider named "dns,sun".
Prior to JDK 7, the first provider that was successfully loaded was used. In JDK 7, providers are chained, which means that if a lookup on a provider fails, the next provider in the list is consulted to resolve the name.
不过我看一下OpenJDK6的代码,貌似也是链状的。
然后我们来看看NameService的定义:
package sun.net.spi.nameservice; import java.net.UnknownHostException; public interface NameService { public java.net.InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException; public String getHostByAddr(byte[] addr) throws UnknownHostException; }
其实是个很简单的接口。默认有两个实现:
匿名类:就是provider名称为default的实现类
DNSNameService
private static NameService createNSProvider(String provider) { if (provider == null) return null; NameService nameService = null; if (provider.equals("default")) { // initialize the default name service nameService = new NameService() { public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException { return impl.lookupAllHostAddr(host); } public String getHostByAddr(byte[] addr) throws UnknownHostException { return impl.getHostByAddr(addr); } }; } else { final String providerName = provider; try { nameService = java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<NameService>() { public NameService run() { Iterator<NameServiceDescriptor> itr = ServiceLoader.load(NameServiceDescriptor.class) .iterator(); while (itr.hasNext()) { NameServiceDescriptor nsd = itr.next(); if (providerName. equalsIgnoreCase(nsd.getType()+"," +nsd.getProviderName())) { try { return nsd.createNameService(); } catch (Exception e) { e.printStackTrace(); System.err.println( "Cannot create name service:" +providerName+": " + e); } } } return null; } } ); } catch (java.security.PrivilegedActionException e) { } } return nameService; }
默认的NameService其实将域名解析操作委托给了impl变量:
static InetAddressImpl impl;
InetAddressImpl本身也是一个接口: