一、设置正向缓存过期时间
(待续)
二、添加自定义的NameService
先上实现步骤:
1. 在META-INF/services下创建一个sun.net.spi.nameservice.NameServiceDescriptor文件
2. 在文件内的一行写上自定义dns服务发现类的全限定类名
3. 自定义dns实现类重写NameServiceDescriptor接口的三个方法,getProviderName返回"XYDnsNameService",getType返回"dns",createNameService返回一个自定义NameService实现类的实例。
4. 自定义XYDnsNameService实现NameService接口的三个方法。
5. 通过代码设置自定义nameservice为默认dns解析器:System.setProperty("sun.net.spi.nameservice.provider.1", "dns,XYDnsNameService");
下面通过源码分析:
当我们使用默认dns解析百度的域名时,方法调用栈如下
InetAddress.getByName(“www.baidu.com”)
InetAddress.getAllByName(“www.baidu.com”)[0]
InetAddress.getAllByName(“www.baidu.com”, null)
InetAddress.getAllByName0(“www.baidu.com”, null, true)
InetAddress.getCachedAddress(“www.baidu.com”)
InetAddress.getCachedAddress(“www.baidu.com”) 从缓存中查不到对应的数据时,就会执行下面的语句
private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check)
throws UnknownHostException {
........
if (addresses == null) {
addresses = getAddressesFromNameService(host, reqAddr);
}
.........
return addresses.clone();
再看看getAddressesFromNameService方法
/* Used to store the name service provider */
private static List nameServices = null;
for (NameService nameService : nameServices) {
try {
addresses = nameService.lookupAllHostAddr(host);
success = true;
break;
} catch (UnknownHostException uhe) {
if (host.equalsIgnoreCase("localhost")) {
InetAddress[] local = new InetAddress[] { impl.loopbackAddress() };
addresses = local;
success = true;
break;
}
else {
addresses = unknown_array;
success = false;
ex = uhe;
}
}
}
getAddressesFromNameService方法遍历nameService(类静态属性),调用nameService的lookupAllHostAddr对域名进行解析。这意味着我们可以通过添加自定义的nameService并重写其lookupAllHostAddr方法来实现自定义的dns解析逻辑。
那么,现在的问题就是向类属性nameService添加自定义nameService
下面的静态代码块的作用是初始化nameService
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();
// 从系统属性属性中获取key为sun.net.spi.nameservice.provider.x的配置的值
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);
}
}
可以看出,如何系统配置sun.net.spi.nameservice.provider.x(x >= 1)不为空的话,就会执行下面的createNSProvider方法
//createNSProvider方法中
final String providerName = provider;
try {
nameService = java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction() {
public NameService run() {
Iterator itr =
// spi加载 META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor里写的全限定包名的类的接口实现类
ServiceLoader.load(NameServiceDescriptor.class的实现类.class)
.iterator();
while (itr.hasNext()) {
// 如果providerName和实现类的getType+,+getProviderName拼接的字符串一致时,调用该实现类的createNameService方法创建一个NameService实例。
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;
}
}
所以我们在设置System.setProperty("sun.net.spi.nameservice.provider.1", "dns,xxxDnsNameService")后,xxxNameServiceDescriptor的getProviderName方法返回"xxxDnsNameService",getType方法返回"dns"。createNameService方法放回一个自定义的NameService实例,这样就实现了自定义dns NameService。