Ao fazer o desenvolvimento de Java, o conhecimento básico com o qual você deve estar familiarizado no mecanismo de carga do carregador. Este artigo resume brevemente o mecanismo de carga de classe Java. Como diferentes implementações de JVM são diferentes, o conteúdo descrito neste artigo é limitado à JVM do ponto de acesso.
Este artigo começará com os quatro aspectos do carregador de classe, modelo de delegação pai fornecido pelo JDK, como personalizar o carregador de classe e os cenários que quebram o mecanismo de delegação dos pais em Java.
JDK Padrão ClassLoader
O JDK fornece os seguintes carregadores de classe por padrão
Carregador de bootstp
O carregador de bootstp está escrito na linguagem C ++. É inicializado após o início da máquina virtual Java. É principalmente responsável por carregar o caminho especificado pelo parâmetro %java_home %/jre/lib, -xbootclasspath e as classes em %java_home %/jre/classes.
Extclassloader
O carregador de bootstp carrega o ExtclassLoader e define o carregador pai do carregador extclass para o bootstrboter.extClassLoader está escrito em java, especificamente, sun.misc.launcher $ extclassloader. O ExtclassLoader carrega principalmente %java_home %/jre/lib/ext, todos os diretórios de classes sob esse caminho e bibliotecas de classes no caminho especificado pela variável do sistema java.nd.dirs.
AppClassLoader
Depois que o carregador de bootstr carrega o ExtclassLoader, o AppClassLoader será carregado e o carregador pai do AppClassLoader é especificado como o ExtclassLoader. AppClassLoader também está escrito em Java. Sua classe de implementação é Sun.misc.launcher $ AppClassLoader. Além disso, sabemos que existe um método getSystemClassLoader no ClassLoader. Este método retorna o AppClassLoader.AppClassLoader é principalmente responsável por carregar o documento de classe ou jar no local especificado pelo ClassPath. É também o carregador de classe padrão para programas Java.
Modelo de delegação de pais
O carregamento de classe de classe em Java adota um mecanismo de delegado dos pais. Ao carregar aulas usando um mecanismo de delegado pai, são adotadas as seguintes etapas:
Atualmente, o ClassLoader verifica primeiro se essa classe foi carregada da classe que já carregou. Se tiver sido carregado, retornará diretamente a classe original.
Cada carregador de classe tem seu próprio cache de carregamento. Quando uma classe é carregada, ela será colocada no cache e pode ser retornada diretamente quando é carregada na próxima vez.
Quando o cache do carregador de classe não é encontrado, o carregador da classe pai é delegado para carregar. O carregador da classe pai adota a mesma estratégia. Primeiro, verifique seu próprio cache e, em seguida, delegue a classe pai da classe pai para carregar, até o botão de classe Bootstrp.
Quando todos os carregadores da classe pai não são carregados, eles são carregados pelo carregador de classe atual e os colocam em seu próprio cache para que possam ser devolvidos diretamente na próxima vez que houver uma solicitação de carregamento.
Falando nisso, você pode se perguntar, por que Java adota um mecanismo de delegação? Para entender esse problema, introduzimos outro conceito de "espaço para nome" sobre o carregador de classe, o que significa que, para determinar uma determinada classe, você precisa de um nome totalmente qualificado da classe e carregar esse carregador de classe para determinar em conjunto. Ou seja, mesmo que os nomes totalmente qualificados das duas classes sejam iguais, porque diferentes carregadores de classe carregam essa classe, é uma classe diferente na JVM. Depois de entender o espaço para nome, vamos dar uma olhada no modelo delegado. Depois de adotar o modelo delegado, os recursos interativos de diferentes carregadores de classe aumentam. Por exemplo, como mencionado acima, as bibliotecas de classes fornecidas pelo nosso JDK Binsheng, como Hashmap, LinkedList, etc. Depois que essas classes são carregadas pelo carregador de classe Bootstr, não importa quantos carregadores de classe existam em seu programa, essas classes podem realmente ser compartilhadas, que evitam confusão causadas por diferentes classes carregando diferentes classes de diferentes classes.
Como personalizar o Classloader
Além do carregador de classe fornecido por padrão mencionado acima, o Java também permite que os aplicativos personalizem o carregador de classe. Se você deseja personalizar o carregador de classe, precisamos implementá -lo herdando java.lang.classloader. Em seguida, vamos dar uma olhada em vários métodos importantes para os quais precisamos prestar atenção ao personalizar o carregador de classe:
1. Método do carregamento
Método da classe de carga declarar
Classe pública <?> LOADCLASS (Nome da String) lança ClassNotFoundException
O exposto acima é a declaração do protótipo do método da classe de carga. A implementação do mecanismo de delegação pai mencionada acima é realmente implementada neste método. Vamos dar uma olhada no código desse método para ver como ele implementa a delegação dos pais.
Método da classe de carga Implementar
classe pública <?> loadclass (nome da string) lança ClassNotFoundException {return loadclass (nome, false);}Pelo acima, podemos ver que o método da classe de carga chama o método loadcclass (nome, false), então vamos dar uma olhada na implementação de outro método de classe de carga.
Classe carregamento (nome da string, resolução booleana)
Classe sincronizada protegida <?> LoadClass (nome da string, resolução booleana) lança ClassNotFoundException {// primeiro, verifique se a classe já foi carregada classe C = findLoadDclass (nome); // verifique se a classe foi carregada se (c == null) {Try {if (! O carregador é especificado, o carregador pai é delegado para carregar. } else {c = findBootStrapClass0 (nome); // Se não houver carregador de classe pai, delegue o carregador de bootstrap para carregar}} catch (classNotFoundException e) {// Se ainda não for encontrado, invoce o findClass em ordem // para encontrar a classe. c = findClass (nome); // Se o carregamento da classe pai não for carregado, ele será carregado por meio de seu próprio findClass. }} if (resolve) {resolveclass (c);} retornar c;}No código acima, adicionei comentários para ver claramente como o mecanismo de delegação dos pais da classe de carga funciona. Uma coisa que precisamos observar aqui é que a classe pública <?> LOADCLASS (nome da string) lança ClassNotFoundException não está marcado como final, o que significa que podemos substituir esse método, o que significa que o mecanismo de delegação de pais pode ser quebrado. Além disso, notamos que existe um método FindClass acima. Em seguida, vamos falar sobre se esse método é ruim.
2.FindClass
Verificamos o código -fonte do java.lang.classloader e descobrimos que a implementação do FindClass é a seguinte:
classe protegida <?> findClass (nome da string) lança classeNotFoundException {throw new ClassNotFoundException (nome);}Podemos ver que a implementação padrão desse método é lançar exceções diretamente, mas, de fato, esse método é deixado para o nosso aplicativo para substituir. A implementação específica depende da sua lógica de implementação. Você pode ler no disco ou obter o fluxo de bytes de arquivos de classe da rede. Depois de obter o binário da classe, você pode entregá -lo para definir o conjunto de carregamento adicional. Vamos descrever o DefinirClass mais tarde. OK, através da análise acima, podemos tirar as seguintes conclusões:
Quando escrevemos nosso próprio carregador de classe, se queremos seguir o mecanismo de delegação dos pais, precisamos apenas substituir o FindClass.
3. DefinirClass
Vamos primeiro olhar para o código -fonte do DefinClass:
DefinirClass
Classe final protegida <?> DefinClass (Nome da String, Byte [] B, Int Off, Int Len) lança ClassFormaterror {Return definitivamenteClass (Nome, B, Off, Len, Null);}A partir do código acima, podemos ver que esse método é definido como final, o que significa que esse método não pode ser substituído. De fato, esta também é a única entrada que resta da JVM. Através dessa entrada exclusiva, a JVM garante que o arquivo de classe deve estar em conformidade com a definição da classe especificada na especificação da máquina virtual Java. Esse método finalmente chamará o método nativo para implementar o carregamento da classe real.
OK, através da descrição acima, vamos pensar sobre a seguinte pergunta:
Se escrevemos uma classe Java.lang.string, podemos substituir a classe que chama o próprio JDK?
A resposta é não. Não podemos alcançá -lo. Por que? Vejo muitas explicações on -line de que o mecanismo de delegação dos pais resolve esse problema, mas na verdade não é muito preciso. Como o mecanismo de delegação dos pais pode ser quebrado, você pode escrever um carregador de classe para carregar a classe java.lang.string que você escreveu, mas você descobrirá que ele não carregará com sucesso. Especificamente, para as aulas que começam com Java.*, A implementação da JVM garantiu que deve ser carregada pelo Bootstrp.
Cenários que não seguem o "mecanismo de delegação dos pais"
O mencionado acima que o mecanismo de delegação pai é principalmente para realizar o problema de interação das classes carregadas entre diferentes carregadores de classe. As classes compartilhadas por todos são entregues ao carregador pai para carregar, mas há de fato uma situação em Java, onde as classes carregadas pelo carregador de classe pai precisam usar classes carregadas pelo carregador filho. Vamos falar sobre a ocorrência dessa situação.
Existe um padrão SPI (ServiceProviderInterface) em Java que usa bibliotecas SPI, como JDBC, JNDI, etc. Todos nós sabemos que o JDBC exige drivers fornecidos por terceiros, e o pacote JAR do motorista é colocado no caminho de classe do nosso próprio aplicativo. A API do JDBC em si faz parte do JDK fornecida pelo JDK e foi carregada pelo Bootstrp. Então, como carrego as classes de implementação fornecidas pelos fabricantes de terceiros? Java apresenta o conceito de carga de classe de contexto de thread. O carregador da classe Thread herdará do thread pai por padrão. Se não for especificado, o padrão é o carregador de classe do sistema (AppClassLoader). Dessa forma, quando um driver de terceiros é carregado, ele pode ser carregado através do carregador de classe de contexto do encadeamento.
Além disso, para implementar o carregador de classe mais flexível OSGI e alguns JavaAppServers, ele também quebra o mecanismo de delegação dos pais.
Resumir
O exposto acima é todo o conteúdo deste artigo sobre a análise de código do uso e uso do mecanismo de carga de classe Java. Espero que seja útil para todos. Amigos interessados podem continuar se referindo a outros tópicos relacionados neste site. Se houver alguma falha, deixe uma mensagem para apontá -la. Obrigado amigos pelo seu apoio para este site!