有一个常见的场景, 咱们在开发过程中,要配置很多的本地host,以实现测试环境一些资源的拜访,那么其它人参加进来开发的话,也得在本人电脑上配置,这样既麻烦,又容易出错。那么能不能把这些配置,写到project中去实现呢, 这样每个人本地的/etc/hosts文件中会很洁净,能够随时clone启动调试而不需任何配置。答案是必定的,那么接下来咱们就应用java反射的形式来实现。咱们次要操作的对象就是java.net.InetAddress,能够从源码中看到, 该类中有两个外围的变量cache和expirySet。
    // mapping from host name to Addresses - either NameServiceAddresses (while    // still being looked-up by NameService(s)) or CachedAddresses when cached    private static final ConcurrentMap<String, Addresses> cache =        new ConcurrentHashMap<>();    // CachedAddresses that have to expire are kept ordered in this NavigableSet    // which is scanned on each access    private static final NavigableSet<CachedAddresses> expirySet =        new ConcurrentSkipListSet<>();

这里会有个疑难, 为啥还有个expirySet,这个问题也能够通过源码失去解决,即为了删除生效的条目。具体含意能够通过浏览正文进行了解。

        // remove expired addresses from cache - expirySet keeps them ordered        // by expiry time so we only need to iterate the prefix of the NavigableSet...        long now = System.nanoTime();        for (CachedAddresses caddrs : expirySet) {            // compare difference of time instants rather than            // time instants directly, to avoid possible overflow.            // (see System.nanoTime() recommendations...)            if ((caddrs.expiryTime - now) < 0L) {                // ConcurrentSkipListSet uses weakly consistent iterator,                // so removing while iterating is OK...                if (expirySet.remove(caddrs)) {                    // ... remove from cache                    cache.remove(caddrs.host, caddrs);                }            } else {                // we encountered 1st element that expires in future                break;            }        }

接下来就是如何来实现反射增加dns条目了,本例中基于java17实现,其它版本会有相应的变动。

        Class<?> cachedAddresses_Class = Class.forName("java.net.InetAddress$CachedAddresses");        Constructor<?> constructor = cachedAddresses_Class.getDeclaredConstructors()[0];        constructor.setAccessible(true);        Object o = constructor.newInstance(host, toInetAddressArray(host, ip), Long.MAX_VALUE);        Field cacheField = InetAddress.class.getDeclaredField("cache");        cacheField.setAccessible(true);        ConcurrentMap<String, Object> cm = (ConcurrentMap<String, Object>) cacheField.get(null);        cm.put(host, o);        Field expirySetField = InetAddress.class.getDeclaredField("expirySet");        expirySetField.setAccessible(true);        ConcurrentSkipListSet<Object> cs = (ConcurrentSkipListSet<Object>) expirySetField.get(null);        cs.add(o);

这样的话, 就能够本人封装一下,比方dns条目都写在一个文件中, 编译打包的时候, 按profile配置决定是否加载。