Pergunta

Estou tentando mudar o carregador de classes em tempo de execução:

public class Test {
    public static void main(String[] args) throws Exception {
        final InjectingClassLoader classLoader = new InjectingClassLoader();
        Thread.currentThread().setContextClassLoader(classLoader);
        Thread thread = new Thread("test") {
            public void run() {
                System.out.println("running...");
                // approach 1
                ClassLoader cl = TestProxy.class.getClassLoader();
                try {
                    Class c = classLoader.loadClass("classloader.TestProxy");
                    Object o = c.newInstance();
                    c.getMethod("test", new Class[] {}).invoke(o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // approach 2
                new TestProxy().test();
            };
        };
        thread.setContextClassLoader(classLoader);
        thread.start();
    }
}

e:

public class TestProxy {
    public void test() {
        ClassLoader tcl = Thread.currentThread().getContextClassLoader();
        ClassLoader ccl = ClassToLoad.class.getClassLoader();
        ClassToLoad classToLoad = new ClassToLoad();
    }
}

(InjetandoClassLoader é uma classe que estende o org.apache.bcel.util.ClassLoader que deve carregar as versões modificadas das classes antes de solicitá-las ao pai)

Eu gostaria de fazer com que o resultado da "abordagem 1" e da "abordagem 2" fosse exatamente igual, mas parece thread.setContextClassLoader(classLoader) não faz nada e a "abordagem 2" sempre usa o carregador de classe do sistema (pode ser determinado comparando as variáveis ​​​​tcl e ccl durante a depuração).

É possível fazer todos classes carregadas por novo thread usam determinado carregador de classe?

Foi útil?

Solução

A classe anônima que você está criando via new Thread("test") { ... } tem uma referência implícita à instância envolvente.Os literais de classe dentro desta classe anônima serão carregados usando o ClassLoader da classe envolvente.

Para fazer este teste funcionar, você deve extrair uma implementação Runnable adequada e carregá-la reflexivamente usando o ClassLoader desejado;em seguida, passe isso explicitamente para o thread.Algo como:

    public final class MyRunnable implements Runnable {
        public void run() {
            System.out.println("running...");
            // etc...
        }
    }

    final Class runnableClass = classLoader.loadClass("classloader.MyRunnable");
    final Thread thread = new Thread((Runnable) runableClass.newInstance());

    thread.setContextClassLoader(classLoader); // this is unnecessary unless you you are using libraries that themselves call .getContextClassLoader()

    thread.start();

Outras dicas

Acho que InjectingClassLoader pode ser importante aqui.Lembre-se de como funciona a delegação de carregamento de classe - se mais de um carregador de classe em sua hierarquia puder encontrar a classe, o carregador de classe superior será aquele que carregará.(Ver Figura 21.2 aqui)

Como InjectingClassLoader não especifica um pai em seu construtor, ele será padronizado para o construtor no ClassLoader abstrato, que definirá o carregador de classe de contexto atual como pai de InjectingClassLoader.Portanto, como o pai (antigo carregador de classe de contexto) pode encontrar TestProxy, ele sempre carrega a classe antes que InjectingClassLoader tenha a chance.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top