Hana 驱动加载问题
问题背景
在适配 SAP Hana 数据源时,使用了 Cobble 提供的类加载机制来加载 JDBC 驱动。然而,在 JDK1.8 环境下加载 Hana Driver 会抛出 java.lang.NoClassDefFoundError: Could not initialize class com.sap.db.jdbc.Driver
,而在更高版本 JDK(如 11/17)上则不会出现该问题。
问题现象
在 JDK1.8 中,运行时加载 Hana Driver 时出现异常:
java.lang.NoClassDefFoundError: Could not initialize class com.sap.db.jdbc.Driver
需要注意两个点:
- 异常发生在运行期,而编译期没有问题,说明 Jar 包已成功加载。
- 问题仅出现在 JDK1.8,JDK11/17 正常,说明与不同版本的 JVM 类加载机制有关。
根据 官方文档,该错误表示 JVM 或 ClassLoader 在运行时试图加载类定义时未找到定义,尽管在编译期是存在的。
问题定位
进一步分析 com.sap.db.jdbc.Driver
的静态代码块。Hana Driver 在静态初始化时会调用 checkJavaVersionMaximum8()
,并尝试注册 Driver。异常表明问题出现在其父类 DriverSapDB
的静态初始化阶段。
static {
checkJavaVersionMaximum8();
INSTANCE = new Driver();
try {
DriverManager.registerDriver(INSTANCE);
} catch (SQLException var1) {
System.err.println("Can't register driver: " + INSTANCE.getClass().getCanonicalName() + ": " + var1.getMessage());
}
pptracer = getTracer().getTraceControl();
}
深入 DriverSapDB
静态代码块后发现,问题发生在:
VERSION_INFO = DriverVersionInfo.newInstance(com.sap.db.jdbc.Driver.class.getPackage().toString());
此时 com.sap.db.jdbc.Driver.class.getPackage()
返回 null,导致 toString()
调用抛出空指针异常。也就是说,Driver 类在加载过程中包信息未正确初始化。
完整静态代码块如下:
static {
...
VERSION_INFO = DriverVersionInfo.newInstance(com.sap.db.jdbc.Driver.class.getPackage().toString());
...
}
原因分析
问题根源在于 Cobble 的自定义类加载器。其 InnerClassLoader.findClass
方法在调用 defineClass
时,未显式调用 definePackage
,导致 Package
信息未被填充。
class InnerClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
String resource = className.replace(".", "/") + ".class";
try {
InputStream inStream = this.resourceLoader.getResourceAsStream(resource);
if (inStream != null) {
ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
ioCopy(inStream, byteOutput);
byte[] bs = byteOutput.toByteArray();
return this.defineClass(className, bs, 0, bs.length);
}
} catch (IOException e2) {
throw new ClassNotFoundException(className, e2);
}
return super.findClass(className);
}
}