简体   繁体   English

在初始化静态对象之前,如何使Classloader评估类路径?

[英]How to Make a Classloader evaluate the classpath before initlializing static objects?

I have an application I am developing that uses both jar libraries and native system libraries. 我有一个正在开发的应用程序,它同时使用jar库和本机系统库。 My problem is the default classloader attempts to load static classes long before the main is called. 我的问题是默认的类加载器会在调用main之前很久就尝试加载静态类。 Because the classpath doesn't yet contain the native libraries the static classes require, a java.lang.NoClassDefFoundError is thrown at the first evaluated static reference. 由于类路径尚未包含静态类所需的本机库,因此在第一个评估的静态引用处将引发java.lang.NoClassDefFoundError。

Here is what my unreachable library loading method looks like: 这是我无法访问的库加载方法的样子:

private static void load_libraries() {
    try {
        String osname = getOSName();

        if (osname == null) {
            throw new RuntimeException("The system you are running on is not supported");
        }
        URL u = NativeLibs.class.getResource(osname);

        URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class urlClass = URLClassLoader.class;
        Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
        method.setAccessible(true);
        method.invoke(urlClassLoader, new Object[]{u});
    } catch (IllegalAccessException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (IllegalArgumentException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (InvocationTargetException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (NoSuchMethodException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    } catch (SecurityException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to load native libs", ex);
    }

}

So, let me explain what is going on here. 所以,让我解释一下这是怎么回事。 First all getOSName() does is return a string representing the OS family I am running on. 首先,getOSName()所做的所有操作都是返回一个表示我正在运行的OS系列的字符串。 It returns null if the OS is unsupported. 如果操作系统不受支持,则返回null。 Just in case I have a problem in that method, I'll go ahead and post it as well: 万一我在该方法上遇到问题,我将继续进行发布:

private static String getOSName() {
    String os = System.getProperty("os.name").toLowerCase(Locale.US);
    if (os.indexOf("win") >= 0) {
        return "windows";
    } else if (os.indexOf("mac os x") >= 0) {
        return "macosx";
    } else if (os.indexOf("nux") >= 0) {
        return "linux";
    } else if (os.indexOf("solaris") >= 0) {
        return "solaris";
    } else {
        return null;
    }
}

The strings it returns are directory names that the method load_libraries() uses to define a relative URL that I pass to the classloader reflectively to load the libraries. 它返回的字符串是目录名,方法load_libraries()使用该目录名定义一个相对URL,我以反射方式传递给类加载器以加载库。 My problem is that the runtime execution never reaches load_libraries(), even though that is the first method in main. 我的问题是,即使这是main中的第一个方法,运行时执行也永远不会到达load_libraries()。

One solution that is obvious is to load native libraries statically with customized jars. 一种显而易见的解决方案是使用自定义的jar静态地加载本机库。 I don't want to statically link the native libraries if it is at all avoidable, because that defeats the purpose of java's platform portability. 我根本不想静态链接本机库,因为这违背了Java平台可移植性的目的。 Another solution I have already deduced is a system executable that can run the applications jar with a determined automatic classpath from the system executable, but still, that solution requires multiple excutables that are platform specific. 我已经推断出的另一个解决方案是一个系统可执行文件,它可以使用来自系统可执行文件的确定的自动类路径来运行应用程序jar,但是该解决方案仍需要多个特定于平台的可执行文件。

So, here is my question: 所以,这是我的问题:

Can I force the classloader to run my Library loading method before it intializes static objects? 我可以强制类加载器在初始化静态对象之前运行我的库加载方法吗? Will this require a customized ClassLoader? 这是否需要定制的ClassLoader? If it does require a custom ClassLoader, will simply passing the system property representing the default classloader solve the problem? 如果确实需要自定义ClassLoader,是否只需传递代表默认Classloader的系统属性即可解决此问题?

Thanks, I hope I wrote a detailed enough question! 谢谢,我希望我写了足够详细的问题!

The reason that "the default classloader attempts to load static classes long before the main is called" is because your main class has static member variables and/or a static initializer. “默认类加载器会在调用main之前很久就尝试加载静态类”的原因是因为您的主类具有静态成员变量和/或静态初始化器。 When the classloader initializes your main class, it must also initialize these static variables. 当类加载器初始化您的主类时,它还必须初始化这些静态变量。 For more information, see the VM Spec . 有关更多信息,请参见VM Spec

The solution -- the only good solution -- is to eliminate static initialization. 解决方案(唯一的方法)是消除静态初始化。 Initialize your static member variables inside your main() , or use a framework such as Spring to do the initialization. main()初始化静态成员变量,或使用诸如Spring之类的框架进行初始化。

In general, there are very few good uses for static member variables. 通常,静态成员变量很少有很好的用途。 Most of them end up being a hack to provide access to resources from arbitrary points in the program. 他们中的大多数最终都是黑客,他们可以从程序中的任意位置访问资源。 This not only makes the program harder to test, but leads to unexpected initialization chains. 这不仅使程序更难测试,而且导致意外的初始化链。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM