JavaRush /Blogue Java /Random-PT /Ideia IntelliJ: Descompilação, Compilação, Substituição (...
Viacheslav
Nível 3

Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas)

Publicado no grupo Random-PT
“Não reinventar a roda” é uma das principais regras para um trabalho bem-sucedido e eficiente. Mas o que fazer quando você não quer reinventar sua própria roda, mas o volante de outra pessoa está torto e as rodas estão quadradas? Esta revisão pretende fornecer uma introdução tão breve quanto possível à técnica de consertar bibliotecas de outras pessoas “como último recurso” e como estender esse assunto para além do seu computador.
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 1

Introdução

Todos nós usamos uma ferramenta ou outra. Mas às vezes as ferramentas não são totalmente adequadas ou apresentam erros. Graças aos recursos da linguagem Java, podemos corrigir o comportamento das ferramentas onde for necessário. É bom quando contribuímos com projetos e enviamos pull requests (você pode ler mais aqui: “ GitHub - Contribuindo com projetos ”). Mas podem não ser aceites imediatamente, ou podem nem sequer ser aceites. Mas para as necessidades do projeto é necessário agora. E aqui, espero, este artigo mostrará as ferramentas disponíveis para nós como desenvolvedores. Precisaremos realizar as seguintes etapas das quais falaremos:
  • Prepare uma aplicação de teste, por exemplo (usando o exemplo de um projeto Hibernate)
  • Encontrar um local mutável
  • Fazendo uma mudança
  • Implantando o repositório
Todas as etapas abaixo são fornecidas para o sistema operacional Windows, mas possuem análogos para sistemas nix. Então você pode repeti-los se necessário.

Preparação do Assunto

Então, precisamos de um projeto de teste. O Hibernate é ideal para nós, porque... é "elegante, moderno e moderno". Não vou entrar em muitos detalhes, porque... O artigo não é sobre o Hibernate. Faremos tudo de forma rápida e direta. E nós, como desenvolvedores adequados, usaremos o sistema de construção. Por exemplo, Gradle também é adequado para nós, que deve ser instalado para este artigo ( https://gradle.org/install/ ). Primeiro, precisamos criar um projeto. O Maven possui arquétipos para isso , e o Gradle possui um plugin especial para isso: Gradle Init . Portanto, abra a linha de comando de qualquer forma que você conheça. Crie um diretório para o projeto, acesse-o e execute o comando:

mkdir javarush 
cd javarush 
gradle init --type java-application
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 2
Antes de importar o projeto, vamos fazer algumas alterações no arquivo que descreve como construir. Este arquivo é chamado de script de construção e é denominado build.gradle. Ele está localizado no diretório em que executamos o gradle init. Portanto, simplesmente o abrimos (por exemplo, no Windows com o comando start build.gradle). Encontramos o bloco “ dependências ” lá, ou seja, dependências. Todos os frascos de terceiros que usaremos estão descritos aqui. Agora precisamos entender o que descrever aqui. Vamos ao site do Hibernate ( http://hibernate.org/ ). Estamos interessados ​​​​no Hibernate ORM . Precisamos da versão mais recente. No menu à esquerda existe uma subseção “Lançamentos”. Selecione “último estável”. Role para baixo e encontre “Implementação principal (inclui JPA)”. Antes era necessário conectar o suporte JPA separadamente, mas agora tudo ficou mais simples e basta apenas uma dependência. Também precisaremos trabalhar com o banco de dados usando o Hibernate. Para fazer isso, vamos usar a opção mais simples - Banco de dados H2 . A escolha está feita, aqui estão nossas dependências:

dependencies {
    // Базовая зависимость для Hibernate (новые версии включают и JPA)
    compile 'org.hibernate:hibernate-core:5.2.17.Final'
    // База данных, к которой мы будем подключаться
    compile 'com.h2database:h2:1.4.197'
    // Use JUnit test framework
    testCompile 'junit:junit:4.12'
}
Ótimo, o que vem a seguir? Precisamos configurar o Hibernate. O Hibernate tem um " Guia de primeiros passos ", mas é estúpido e mais atrapalha do que ajuda. Portanto, vamos direto ao “ Guia do Usuário ” como as pessoas certas. No índice vemos a seção “ Bootstrap ”, que se traduz como “Bootstrapping”. Exatamente o que você precisa. Há muitas palavras inteligentes escritas lá, mas a questão é que deve haver um diretório META-INF no caminho de classe e um arquivo persistence.xml. De acordo com o padrão, o classpath contém o diretório “recursos”. Portanto, criamos o diretório especificado: mkdir src\main\resources\META-INF Crie o arquivo persistence.xml lá e abra-o. Lá na documentação há um exemplo “Exemplo 268. Arquivo de configuração META-INF/persistence.xml” do qual pegaremos o conteúdo e inseriremos no arquivo persistence.xml. Em seguida, inicie o IDE e importe nosso projeto criado para ele. Agora precisamos salvar algo no banco de dados. Isso é algo chamado de entidade. As entidades representam algo do chamado modelo de domínio. E no índice, vejam só, vemos “ 2. Modelo de Domínio ”. Descemos o texto e vemos no capítulo “2.1.Mapeamento de tipos” um exemplo simples de entidade. Vamos levar isso para nós mesmos, encurtando um pouco:
package entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity(name = "Contact")
public class Contact {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    public Contact(String name) {
        this.name = name;
    }
}
Agora temos uma classe que representa uma entidade. Vamos voltar ao persistence.xml e corrigir um lugar ali: Onde indicado, classindicaremos nossa classe entity.Contact. Ótimo, só falta lançar. Voltemos ao capítulo Bootstrap . Como não temos um servidor de aplicativos que nos forneça um ambiente EE especial (ou seja, um ambiente que implemente determinado comportamento do sistema para nós), trabalhamos em um ambiente SE. Para isso, apenas o exemplo “Exemplo 269. EntityManagerFactory inicializado pelo aplicativo” é adequado para nós. Por exemplo, vamos fazer isso:
public class App {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("CRM");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        Contact contact = new Contact("Vasya");
        em.persist(contact);
        em.getTransaction().commit();
        Query sqlQuery = em.createNativeQuery("select count(*) from contact");
        BigInteger count = (BigInteger) sqlQuery.getSingleResult();
        emf.close();
        System.out.println("Entiries count: " + count);
    }
}
Viva, nosso assunto está pronto. Eu não queria omitir essa parte , porque... Para os capítulos seguintes, é aconselhável entender como surgiu o nosso assunto.

Encontrando Comportamento Modificável

Vamos tomar o lugar da inicialização do campo de contagem do tipo BigInteger e definir pontos de interrupção lá ( BreakPoint ). Depois de inserir a linha desejada, isso pode ser feito usando Ctrl+F8 ou através do menu Run -> Toggle Line Breakpoint. Então executamos nosso método principal no debug (Run -> Debug):
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 3
Um exemplo um pouco desajeitado, mas digamos que queremos alterar o número de espaços de consulta na inicialização. Como podemos ver, nosso sqlQuery é NativeQueryImpl. Clique em Ctrl+N, escreva o nome da turma e vá até ela. Para que quando formos para uma aula, sejamos transferidos para o local onde esta aula está localizada e ativemos a rolagem automática:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 4
Observemos imediatamente que o Idea atualmente não sabe onde encontrar o código-fonte do programa (isto é, código-fonte). Portanto, ela gentilmente descompilou o conteúdo do arquivo de classe para nós:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 5
Observe também que no título da janela do IntelliJ Idea está escrito onde Gradle salva o artefato para nós. Agora, vamos dar uma ideia do caminho onde nosso artefato está localizado:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 6
Vamos para este diretório na linha de comando usando o comando cd way to каталогу. Vou fazer uma observação imediatamente: se é possível construir um projeto a partir do código-fonte, é melhor compilar a partir do código-fonte. Por exemplo, o código-fonte do Hibernate está disponível no site oficial. É melhor escolhê-lo para a versão desejada e fazer todas as alterações lá e montá-lo usando os scripts de construção que estão especificados no projeto. Apresento no artigo a opção mais terrível - existe um jar, mas não há código-fonte. E nota número 2: Gradle pode obter o código-fonte usando plug-ins. Consulte Como baixar javadocs e fontes para jar usando Gradle para obter detalhes .

Fazendo uma mudança

Precisamos recriar a estrutura de diretórios de acordo com o pacote em que está a classe que estamos alterando. Neste caso: mkdir org\hibernate\query\internal, após o qual criamos um arquivo neste diretório NativeQueryImpl.java. Agora abrimos esse arquivo e copiamos todo o conteúdo da classe do IDE (o mesmo que o Idea descompilou para nós). Altere as linhas necessárias. Por exemplo:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 7
Agora, vamos compilar o arquivo. Nós fazemos: javac org\hibernate\query\internal\NativeQueryImpl.java. Uau, você não pode simplesmente pegá-lo e compilá-lo sem erros. Recebemos vários erros de Não é possível encontrar o símbolo porque... a classe mutável está vinculada a outras classes, que o IntelliJ Idea geralmente adiciona ao caminho de classe para nós. Você sente toda a utilidade dos nossos IDEs? =) Bem, vamos adicionar nós mesmos, podemos fazer isso também. Vamos copiar os caminhos para:
  • [1] - hibernate-core-5.2.17.Final.jar
  • [2] - hibernate-jpa-2.1-api-1.0.0.Final.jar
Assim como fizemos: Na visualização “Projeto” em “Bibliotecas externas” encontramos o jar necessário e clicamos em Ctrl+Shift+C. Agora vamos criar e executar o seguinte comando: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java Como resultado, novos arquivos de classe aparecerão próximos ao arquivo java, que precisam ser atualizados no arquivo jar:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 8
Viva, agora você pode realizar a atualização do jar. Podemos ser guiados por materiais oficiais : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea provavelmente não permitirá que você altere arquivos. Portanto, antes de realizar a atualização do jar, provavelmente você terá que fechar o Idea e, após a atualização, abri-lo. Depois disso, você pode reabrir o IDE e executar o dubug novamente. Os pontos de interrupção não são redefinidos entre as reinicializações do IDE. Portanto, a execução do programa irá parar onde estava antes. Voila, vemos como nossas mudanças funcionam:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 9
Ótimo. Mas aqui surge a pergunta - por quê? Simplesmente pelo fato de que quando o gradle constrói um projeto, ele analisa o bloco de dependências e repositórios. O Gradle possui um determinado cache de compilação, que está localizado em um determinado local (consulte “ Como definir o local do cache do Gradle? ” Se não houver dependência no cache, o Gradle fará o download do repositório. Como alteramos o jar no cache em si, então Gradle pensa que a biblioteca está no cache e não bombeia nada. Mas qualquer limpeza do cache fará com que nossas alterações sejam perdidas. Além disso, ninguém além de nós pode simplesmente ir buscá-las. Quanta inconveniência , não é? O que fazer. Hmm, downloads do repositório? Então precisamos do nosso repositório, com preferências e poetisas. Este é o próximo passo.

Implantando o repositório

Existem diferentes soluções gratuitas para implantar seu repositório: uma delas é Artifactory e a outra é Apache Archive . O Artifactory parece elegante, estiloso, moderno, mas tive dificuldades com ele, não queria posicionar os artefatos corretamente e gerava metadados maven errados. Portanto, inesperadamente para mim, a versão Apache funcionou para mim. Acabou não sendo tão bonito, mas funciona de forma confiável. Na página de download , procure a versão Standalone e descompacte-a. Eles têm seu próprio " Início Rápido ". Após o lançamento, você precisa aguardar até o endereço http://127.0.0.1:8080/#repositorylist. Depois disso, selecione "Carregar Artefato":
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 10
Clique em “Iniciar upload” e depois em “Salvar arquivos”. Após isso, uma mensagem verde de sucesso aparecerá e o artefato ficará disponível na seção “Navegar”. Isso deve ser feito para arquivos jar e pom:
Ideia IntelliJ: Descompilação, Compilação, Substituição (ou como corrigir os erros de outras pessoas) - 11
Isso se deve ao fato de que dependências adicionais de hibernação são especificadas no arquivo pom. E só nos resta 1 passo - especifique o repositório em nosso script de construção:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
E, consequentemente, a versão do nosso hibernate será: compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. Só isso, agora nosso projeto usa a versão que corrigimos, e não a original.

Conclusão

Parece que nos conhecemos. Espero que tenha sido interessante. Esses “truques” raramente são feitos, mas se de repente seus requisitos de negócios estabelecerem condições que as bibliotecas que você usa não podem satisfazer, você sabe o que fazer. E sim, aqui estão alguns exemplos que podem ser corrigidos desta forma:
  • Existe um servidor web chamado Undertow. Até algum tempo existia um bug que, ao utilizar um proxy, não permitia descobrir o IP do usuário final.
  • Por enquanto, o WildFly JPA tratou de certa forma um momento não levado em consideração pela especificação, por isso foram lançadas exceções. E não era configurável.
#Viacheslav
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION