Confundir
No passado, ao olhar para o código -fonte, eu sempre encontrei o código na estrutura usando thread.currentThread.getContextClassLoader () para obter o carregador de classe de contexto do encadeamento atual e usar esse carregador de classe de contexto para carregar a classe.
Quando geralmente escrevemos código em um programa, quando queremos carregar classes dinamicamente, geralmente usamos classe.ForName () para carregar as classes de que precisamos. Por exemplo, o mais comum é que, quando estamos programando o JDBC, usamos classe.ForName () para carregar o driver JDBC.
tente {return Class.ForName ("oracle.jdbc.driver.oracledriver");} Catch (ClassNotFoundException e) {// Skip}Então, por que quando usamos classe.ForName () para carregar uma classe, se a classe não puder ser encontrada, ainda tentamos carregar a classe usando o carregador de classe obtido por Thread.CurrentThread.getContextLoader ()? Por exemplo, podemos encontrar o seguinte código:
tente {return class.ForName (ClassName);} Catch (ClassNotFoundException e) {// Skip} classLoader ctxClassloader = Thread.currentThread (). getContextClassLoader (); if (ctxclassloader! = null) {try {clazz = ctxclassloader.loadclass (className); } catch (classNotfoundException e) {// Skip}}A parte em negrito aqui é usar o carregador obtido pelo Thread.currentThread.getContextLoader () para carregar a classe. Obviamente, o carregador de classe usado quando classe.ForName () carrega a classe pode ser diferente do carregador de classe obtido por Thread.CurrentThread.getContextLoader (). Então, por que a diferença ocorre?
Carregador de classe java
Antes de entender por que esse carregador de classe obtido pelo Thread.CurrentThread.getContextLoader () é usado, vamos primeiro entender o carregador de classe (classe de classe) usado na JVM.
Existem três tipos de carregadores de classe na JVM por padrão:
Carregador de classe de bootstrap
O carregador de classe da classe de bootstrap é um carregador de classe embutido no JDK, usado para carregar classes dentro do JDK. O carregador de classe de bootstrap é usado para carregar as classes abaixo de $ java_home/jre/lib no JDK, como as classes no pacote RT.jar. O carregador de classe Bootstrap faz parte da JVM e geralmente é escrito no código nativo.
Carregador de classe de extensão
O carregador de classe da classe de extensão da classe é usado principalmente para carregar classes no pacote de extensão JDK. Geralmente, os pacotes sob $ java_home/lib/ext são carregados através desse carregador de classe e as classes deste pacote começam basicamente com o Javax.
Carregador de classe do sistema
O carregador de classe da classe do sistema também é chamado de carregador de classe de aplicativo (AppClassLoader). Como o nome sugere, esse carregador de classe é usado para carregar o código do aplicativo que os desenvolvedores geralmente escrevem. O carregador de classe do sistema é usado para carregar classes no nível do aplicativo armazenadas no caminho da classe de classe.
O código a seguir lista essas três carregadeiras de classe:
classe pública mainClass {public static void main (string [] args) {System.out.println (Integer.class.getClassLoadeler ()); System.out.println (logging.class.getClassLoader ()); System.out.println (mainClass.class.getclassloader ()); }}Onde obter o carregador de classe de bootstrap retorna valor nulo para sempre
NULL # Bootstrap Classe Loader Sun.misc.launcher$ExtClassloadler@5E2DE80C # Classe de extensão Loader Sun.misc.launcher$AppClassloader@18b4aac2 # Sistema Classe carregador de classe System
Modelo de delegação de pais
Os três carregadores de tipo introduzidos acima não estão isolados, eles têm um relacionamento hierárquico:
Os três carregadores de classe trabalham juntos através desse relacionamento hierárquico e são responsáveis pelo carregamento de classes juntos. O modelo hierárquico acima é chamado de modelo "Delegação de Pais" do carregador de classe. O modelo de delegação dos pais exige que todos os carregadores de classe tenham um carregador de pais, exceto o carregador de classe de bootstrap de nível superior. Quando o carregador de classe carrega uma classe, verifique se há classes no cache que foram carregadas. Caso contrário, o carregador pai é delegado para carregar a classe primeiro. O carregador pai executa o mesmo trabalho que o carregador filho anterior até que a solicitação atinja o carregador de classe de bootstrap de nível superior. Se o carregador pai não puder carregar a classe necessária, o carregador filho tentará carregar a classe por si só. Funciona semelhante ao seguinte:
Podemos usar o código no Classloader no JDK para ver a implementação do mecanismo de delegação pai. O código é implementado em Classloader.loadclass ()
Classe girada <?> LOADCLASS (nome da string, resolução booleana) lança ClassNotFoundException Sincronized (getClassLoadingLock (name)) {// primeiro, verifique se a classe já foi carregada classe <?> c = findloadLoadClass (nome); if (c == null) {long t0 = System.nanotime (); tente {if (parent! = null) {c = parent.loadclass (nome, false); } else {c = findBootStrapClassornull (nome); }} catch (classNotFoundException e) {// classNotfoundException arremessado se a classe não encontrada // da classe pai não null carregador} if (c == null) {// Se ainda não for encontrado, invoce findClass em ordem // para encontrar a classe. T1 longo = System.Nanotime (); c = findClass (nome); // Este é o carregador de classe definidor; registre as estatísticas sol.misc.perfcounter.getParentDelegationTime (). Addtime (t1 - t0); Sun.misc.perfcounter.getfindclasSTime (). addelapSedTimeFrom (T1); sun.misc.perfcounter.getfindClasses (). increment (); }} if (resolve) {resolveclass (c); } retornar c; }Uma vantagem de usar a delegação de pais para organizar carregadores de classe é ser seguro. Se definirmos uma classe de string, esperamos substituir esta classe String pela implementação de java.lang.string no java padrão.
Colocamos o arquivo de classe da classe String que implementamos no caminho da classe. Quando usamos o carregador de classe para carregar a classe String que implementamos, primeiro, o carregador de classe delegará a solicitação ao carregador pai e, através da camada por camada, o carregador de classe Bootstrap finalmente carregará o tipo de string no pacote rt.jar e depois o retornará. Nesse processo, nosso carregador de classe ignora a classe String que colocamos no caminho de classe.
Se o mecanismo de delegação dos pais não for adotado, o carregador de classe do sistema poderá encontrar o arquivo da classe String no caminho da classe e carregá -lo no programa, fazendo com que a implementação da string no JDK seja substituída. Portanto, esse método de trabalho de carregadeiras de classe garante que os programas Java possam ser executados com segurança e estágio até certo ponto.
Carregador de classe de contexto de thread
O acima fala sobre tantos conteúdos relacionados a carregadeiras de classe, mas ainda não fala sobre o tópico de hoje, os carregadores de classe de contexto de encadeamento.
Neste ponto, já sabemos que o Java fornece três carregadores de tipo e trabalha em conjunto de acordo com um mecanismo estrito de delegação de pais. Na superfície, parece perfeito, mas é esse mecanismo estrito de delegação de pais que causa algumas limitações ao carregar as classes.
Quando nossa estrutura mais básica precisa usar classes no nível do aplicativo, podemos usar apenas essas classes se essa classe for carregada quando o carregador de classe usado por nossa estrutura atual puder ser carregado. Em outras palavras, não podemos usar o carregador de classe do carregador de classe atual. Essa limitação é causada pelo mecanismo de delegação pai, porque a delegação de solicitações de carregamento de classe é unidirecional.
Embora não haja muitos casos, ainda existe essa demanda. Um serviço JNDI típico. O JNDI fornece uma interface para consultar recursos, mas a implementação específica é implementada por diferentes fabricantes. No momento, o código do JNDI é carregado pelo carregador de classe de bootstrap da JVM, mas a implementação específica é diferente do JDK fornecido pelo usuário, para que ele possa ser carregado apenas pelo carregador de classe do sistema ou por outro carregador de classe definido pelo usuário. Sob o mecanismo de delegação dos pais, o JNDI não pode obter a implementação do SPI da JNDI.
Para resolver esse problema, é introduzido um carregador de classe de contexto de thread. SetContextClassLoader () da classe java.lang.thread setContextClassLoader () (se não estiver definido, ele será herdado do thread pai por padrão. Se o programa não o definir, ele será inadimplente para o carregador de classe do sistema). Com o carregador de classe de contexto do thread, o aplicativo pode passar o carregador de classe usado pelo aplicativo para o código que usa o carregador de classe de nível superior através do java.lang.thread.setContextClassloader (). Por exemplo, o serviço JNDI acima pode usar esse método para obter um carregador de classe que pode carregar implementações SPI e obter as classes de implementação SPI necessárias.
Pode -se observar que a introdução de carregadores de classe rosqueada é na verdade uma destruição do mecanismo de delegação pai, mas fornece flexibilidade no carregamento de classe.
Resolva dúvidas
De volta ao início, para carregar classes implementadas por usuários fora da estrutura, essas classes não podem ser carregadas através do carregador de classe usado pela estrutura. Para ignorar o modelo de delegação pai do carregador de classe, Thread.getContextClassLoader () é usado para carregar essas classes.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.