多个类加载器和单例

时间:2015-07-07 18:14:22

标签: java singleton classloader

我最近了解到java程序可以有多个类加载器。我找到了一些StackOverflow帖子,解释了如何创建单例对象。

常见的方法是这样的:

if (instance == null) {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      if (classLoader == null) {
        classLoader = Singleton.class.getClassLoader();
      }
      Class<?> sclass = (classLoader.loadClass(Singleton.class.getCanonicalName()));
}

我不太明白这是如何解决多个类加载器的问题。如果您有多个ClassLoader,则当每个实例加载该类时,该实例将为null。这不对吗?如果我能得到一个关于它是如何工作的简短解释,我会觉得很有帮助。

此外,我们在什么时候能够检索实际的Singleton实例?我可以执行instance = (Singleton) sclass.newInstance();之类的操作,但这会为每个ClassLoader创建一个新实例。

2 个答案:

答案 0 :(得分:0)

  

如何解决多个类加载器的问题。

我不知道你指的是哪个问题,但它没有解决任何问题。

  

当每个实例都尝试加载该类时,该实例将为null。

实例不会为空。

  

我们能够检索实际的Singleton实例吗?

当线程具有所需的ClassLoader时,您可以使用其中任何一个获取SIngleton实例。

Singleton s = Singleton.INSTANCE;

Singleton s = Singleton.getInstance();

取决于您正常访问Singleton的方式。如果你需要另一个ClassLoader中的singelton,你必须做同样的事情,除非通过反射。

答案 1 :(得分:0)

有很多例子,但它们似乎是从JavaWorld上的文章中复制而来。

它没有摆脱多个类加载器的问题。您仍然可以拥有多个实例。

它尝试通过线程上下文类加载器加载类。问题是你不能依赖于任何特定的类加载器,或者每个线程或者调用它的地方都是相同的。如果这不可用,那么它会尝试Singleton.class.getClassLoader()这是将要使用的类加载器,并且不保证每次都是相同的。

如果上下文类加载器始终相同,那么每次都会获得相同的实例,但是您需要通过反射访问此实例。您无法将其转换为正确的类型,或者您获得ClassCastException,因为加载该类的类加载器与当前类不匹配。

以下示例演示getClass方法的用法,使用ByteBuddy库创建类加载器:

public class Main {
    public static void main(String[] args) throws Exception {
        run();
        run();
    }

    public static void run() throws Exception {
        ClassLoader parent = Main.class.getClassLoader();

        byte[] runnerBytes = Files.readAllBytes(new File("/path/to/bin/Runner.class").toPath());
        byte[] singletonBytes = Files.readAllBytes(new File("/path/to/bin/Singleton.class").toPath());

        Map<String, byte[]> classes = new HashMap<>();
        classes.put("Runner", runnerBytes);
        classes.put("Singleton", singletonBytes);

        ByteArrayClassLoader classLoader = new ByteArrayClassLoader.ChildFirst(
            parent,
            classes,
            null,
            ByteArrayClassLoader.PersistenceHandler.MANIFEST,
            PackageDefinitionStrategy.NoOp.INSTANCE
        );

        Class<?> clazz = classLoader.loadClass("Runner");

        Thread.currentThread().setContextClassLoader(classLoader);
        Object instance = clazz.newInstance();
        clazz.getMethod("run").invoke(instance);
    }
}

public class Runner {
    public void run() {
        try {
            Object instance = getClass("Singleton").getMethod("getInstance").invoke(null);
            Object result = instance.getClass().getMethod("getValue").invoke(instance);
            System.out.println("RESULT: " + result);
        } catch(ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }

    private static Class<?> getClass(String className) throws ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if(classLoader == null) {
            classLoader = Singleton.class.getClassLoader();
        }
        return (classLoader.loadClass(className));
    }
}

public class Singleton {
    private static final Singleton instance = new Singleton();
    private double value = Math.random();

    public static Singleton getInstance() {
        return instance;
    }

    public double getValue() {
        return value;
    }
}

结果:

RESULT: 0.8998675708591397
RESULT: 0.7230302140857906

所以这不会创建相同的实例。