一个java web项目启动时遇到了下面这么个报错: java.util.prefs.WindowsPreferences.WindowsRegOpenKey1 Trying to recreate Windows registry node Software\JavaSoft\Prefs at root 0x80000002 很诡异的一个报错,怎么会与windows注册表相关,这个报错是一个配置文件属性加载引起的,百度很快找到了解决方式: 在注册表项 HKEY_LOCAL_MACHINE\Software\JavaSoft 下面新建 Prefs 项目即可。
问题是解决了,但是怎么会出现这么个报错?于是找到了配置文件的引入部分:
1 2 3 4 5 6 7 8 <bean id ="propertyConfigurer" class = "org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer" > <property name ="locations" > <list > ... </list > </property > </bean >
这里解析配置文件用的是 PreferencesPlaceholderConfigurer 这个类,该类扩展了 PropertyPlaceholderConfigurer 类,添加了windows下用户路径和系统路径的解析。 看下该类的解析配置文件的核心方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private Preferences systemPrefs;private Preferences userPrefs; ......@Override protected String resolvePlaceholder (String placeholder, Properties props) { String path = null ; String key = placeholder; int endOfPath = placeholder.lastIndexOf('/' ); if (endOfPath != -1 ) { path = placeholder.substring(0 , endOfPath); key = placeholder.substring(endOfPath + 1 ); } String value = resolvePlaceholder(path, key, this .userPrefs); if (value == null ) { value = resolvePlaceholder(path, key, this .systemPrefs); if (value == null ) { value = props.getProperty(placeholder); } } return value; }
Preferences是一个保存系统和用户配置信息的一个类,同时有下面的子类扩展关系: Preferences <- AbstractPreferences <- WindowsPreferences 上面的方法会判断最后一个 / 位置,其实就是判断key中是否包含路径,如果包含就会截取出路径去解析用户和系统的配置,最后都找不到再从 Properties 中获取。
继续看 resolvePlaceholder 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected String resolvePlaceholder (String path, String key, Preferences preferences) { if (path != null ) { try { if (preferences.nodeExists(path)) { return preferences.node(path).get(key, null ); } else { return null ; } } catch (BackingStoreException ex) { throw new BeanDefinitionStoreException ( "Cannot access specified node path [" + path + "]" , ex); } } else { return preferences.get(key, null ); } }
nodeExists 方法是 AbstractPreferences 中的一个判断系统路径是否存在的一个方法,追踪调用链可以发现这个方法最终调用了 定义在 Preferences 中的 childrenNamesSpi() 方法,在 WindowsPreferences 中重写了该方法,该方法中我们可以看到最终抛出 BackingStoreException 的地方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected String[] childrenNamesSpi() throws BackingStoreException { int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE); if (nativeHandle == NULL_NATIVE_HANDLE) { throw new BackingStoreException ( "Could not open windows registry node " + byteArrayToString(windowsAbsolutePath()) + " at root 0x" + Integer.toHexString(rootNativeHandle()) + "." ); } ...... ...... }
同时,在该类的定义中我们可以看到在windows中系统根路径被定义为:
1 2 private static final byte [] WINDOWS_ROOT_PATH = stringToByteArray("Software\\JavaSoft\\Prefs" );
这也就是为什么解决方法中需要添加这个注册表项了。 PS:用户路径为 HKEY_CURRENT_USER\Software\JavaSoft\Prefs
回过头再来看这个问题,首先配置文件key中出现了 / 符号,同时解析配置文件的类用了 PreferencesPlaceholderConfigurer 而不是常规的 PropertyPlaceholderConfigurer 导致Spring同时解析用户和系统环境中的配置,而windows中默认系统根路径注册表中不存在,这才导致了问题的产生。
按照道理来说这两个注册表项应该是jdk或者jre安装的时候创建的,于是google之,发现了下面的一个issue:https://bugs.openjdk.java.net/browse/JDK-8139507 里面提到了出现这个问题的原因: jre的一个bug,某个版本之前是正常的,没开启windwos UAC。
本站所有文章除特别声明外,均采用 BY-NC-SA 4.0 许可协议。转载请注明出处!