JavaRush /Blog Java /Random-ES /Idea IntelliJ: descompilación, compilación, sustitución (...
Viacheslav
Nivel 3

Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas)

Publicado en el grupo Random-ES
“No reinventar la rueda” es una de las principales reglas para un trabajo exitoso y eficiente. Pero, ¿qué hacer cuando no quieres reinventar tu propio volante, pero el volante de otra persona resulta torcido y las ruedas cuadradas? Esta revisión pretende proporcionar una introducción lo más breve posible a la técnica de arreglar las bibliotecas de otras personas "como último recurso" y cómo extender esto a su computadora.
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 1

Introducción

Todos utilizamos una herramienta u otra. Pero a veces las herramientas no son del todo adecuadas o tienen errores. Gracias a las características del lenguaje Java podemos corregir el comportamiento de las herramientas donde lo necesitemos. Es bueno cuando contribuimos a proyectos y enviamos solicitudes de extracción (puedes leer más aquí: " GitHub - Contribuir a proyectos "). Pero es posible que no se acepten de inmediato o que ni siquiera se acepten. Pero para las necesidades del proyecto es necesario ahora. Y aquí, espero, este artículo mostrará las herramientas disponibles para nosotros como desarrolladores. Necesitaremos realizar los siguientes pasos de los que os hablaremos:
  • Prepare una aplicación de prueba, por ejemplo (usando el ejemplo de un proyecto de Hibernate)
  • Encontrar una ubicación cambiable
  • haciendo un cambio
  • Implementando el repositorio
Todos los pasos a continuación se dan para el sistema operativo Windows, pero tienen análogos para los sistemas Nix. Para que puedas repetirlos si es necesario.

Preparación de la materia

Entonces, necesitamos un proyecto de prueba. Hibernate es ideal para nosotros, porque... es "elegante, moderno y moderno". No entraré en demasiados detalles, porque... El artículo no trata sobre Hibernate. Haremos todo rápidamente y al grano. Y nosotros, como verdaderos desarrolladores, utilizaremos el sistema de compilación. Por ejemplo, Gradle también es adecuado para nosotros, que debe instalarse para este artículo ( https://gradle.org/install/ ). Primero, necesitamos crear un proyecto. Maven tiene arquetipos para esto y Gradle tiene un complemento especial para esto: Gradle Init . Entonces, abra la línea de comando de cualquier forma que conozca. Cree un directorio para el proyecto, vaya a él y ejecute el comando:

mkdir javarush 
cd javarush 
gradle init --type java-application
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 2
Antes de importar el proyecto, hagamos algunos cambios en el archivo que describe cómo compilarlo. Este archivo se llama script de compilación y se denomina build.gradle. Está ubicado en el directorio en el que ejecutamos gradle init. Por tanto, simplemente lo abrimos (por ejemplo, en Windows con el comando start build.gradle). Allí encontramos el bloque “ dependencias ”, es decir dependencias. Todos los archivos jar de terceros que usaremos se describen aquí. Ahora necesitamos entender qué describir aquí. Vayamos al sitio web de Hibernate ( http://hibernate.org/ ). Estamos interesados ​​en Hibernate ORM . Necesitamos la última versión. En el menú de la izquierda hay una subsección "Lanzamientos". Seleccione "último estable". Desplácese hacia abajo y busque "Implementación principal (incluye JPA)". Anteriormente, era necesario conectar el soporte JPA por separado, pero ahora todo se ha vuelto más sencillo y solo una dependencia es suficiente. También necesitaremos trabajar con la base de datos usando Hibernate. Para hacer esto, tomemos la opción más simple: Base de datos H2 . La elección está hecha, aquí están nuestras dependencias:

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'
}
Genial, ¿qué sigue? Necesitamos configurar Hibernate. Hibernate tiene una " Guía de introducción ", pero es estúpida y más un obstáculo que una ayuda. Por tanto, vayamos directamente a la “ Guía del usuario ” como las personas adecuadas. En el índice vemos la sección " Bootstrap ", que se traduce como "Bootstrapping". Justo lo que necesitas. Hay muchas palabras inteligentes escritas allí, pero el punto es que debería haber un directorio META-INF en el classpath y debería haber un archivo persistence.xml. Según el estándar, el classpath contiene el directorio "recursos". Por lo tanto, creamos el directorio especificado: mkdir src\main\resources\META-INF Cree allí el archivo persistence.xml y ábralo. Allí en la documentación hay un ejemplo “Ejemplo 268. Archivo de configuración META-INF/persistence.xml” del cual tomaremos el contenido y lo insertaremos en el archivo persistence.xml. A continuación, inicie el IDE e importe nuestro proyecto creado en él. Ahora necesitamos guardar algo en la base de datos. Esto es algo llamado entidad. Las entidades representan algo del llamado modelo de dominio. Y en la tabla de contenido, he aquí, vemos " 2. Modelo de dominio ". Bajamos el texto y vemos en el capítulo “2.1. Tipos de mapeo” un ejemplo sencillo de una entidad. Llevémoslo a nosotros mismos, acortándolo un poco:
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;
    }
}
Ahora tenemos una clase que representa una entidad. Volvamos a persistence.xml y corrijamos un lugar allí: Donde se indique, classindicaremos nuestra clase entity.Contact. Genial, solo queda lanzar. Volvamos al capítulo de Bootstrap . Como no tenemos un servidor de aplicaciones que nos proporcione un entorno EE especial (es decir, un entorno que implemente cierto comportamiento del sistema para nosotros), trabajamos en un entorno SE. Para esto, solo el ejemplo "Ejemplo 269. EntityManagerFactory arrancado de la aplicación" es adecuado para nosotros. Por ejemplo, hagamos esto:
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);
    }
}
Hurra, nuestro tema está listo. No quería omitir esta parte , porque... Para los siguientes capítulos, es aconsejable comprender cómo surgió nuestro tema.

Encontrar un comportamiento modificable

Tomemos el lugar de inicialización del campo de recuento de tipo BigInteger y establezcamos puntos de interrupción allí ( BreakPoint ). Habiendo insertado en la línea deseada, esto se puede hacer usando Ctrl+F8 o mediante el menú Ejecutar -> Alternar punto de interrupción de línea. Luego ejecutamos nuestro método principal en depuración (Ejecutar -> Depurar):
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 3
Un ejemplo un poco torpe, pero digamos que queremos cambiar la cantidad de espacios de consulta al inicio. Como podemos ver, nuestro sqlQuery es NativeQueryImpl. Haz clic en Ctrl+N, escribe el nombre de la clase y accede a ella. De modo que cuando vayamos a una clase, seremos transferidos al lugar donde se encuentra esta clase y activaremos el desplazamiento automático:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 4
Notemos inmediatamente que Idea no sabe actualmente dónde encontrar el código fuente del programa (es decir, el código fuente). Por lo tanto, ella amablemente descompiló el contenido del archivo de clase para nosotros:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 5
Tenga en cuenta también que en el título de la ventana de IntelliJ Idea está escrito dónde Gradle guarda el artefacto para nosotros. Ahora, entremos en Idea la ruta donde se encuentra nuestro artefacto:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 6
Vayamos a este directorio en la línea de comando usando el comando cd manera de каталогу. Tomaré una nota de inmediato: si es posible construir un proyecto desde el código fuente, es mejor hacerlo desde el código fuente. Por ejemplo, el código fuente de Hibernate está disponible en el sitio web oficial. Es mejor seleccionarlo para la versión deseada y realizar todos los cambios allí y ensamblarlo utilizando los scripts de compilación que se especifican en el proyecto. En el artículo presento la opción más terrible: hay un frasco, pero no hay código fuente. Y nota número 2: Gradle puede obtener el código fuente mediante complementos. Consulte Cómo descargar javadocs y fuentes para jar usando Gradle para obtener más detalles .

haciendo un cambio

Necesitamos recrear la estructura del directorio de acuerdo con el paquete en el que se encuentra la clase que estamos cambiando. En este caso:, mkdir org\hibernate\query\internaltras lo cual creamos un archivo en este directorio NativeQueryImpl.java. Ahora abrimos este archivo y copiamos allí todo el contenido de la clase del IDE (el mismo que descompiló Idea para nosotros). Cambie las líneas necesarias. Por ejemplo:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 7
Ahora, compilemos el archivo. Hacemos: javac org\hibernate\query\internal\NativeQueryImpl.java. Vaya, no puedes simplemente tomarlo y compilarlo sin errores. Recibimos un montón de errores de No se puede encontrar el símbolo, porque... la clase mutable está vinculada a otras clases, que IntelliJ Idea generalmente agrega al classpath por nosotros. ¿Sientes toda la utilidad de nuestros IDE? =) Bueno, vamos a agregarlo nosotros, también podemos hacerlo. Copiemos las rutas para:
  • [1] - hibernate-core-5.2.17.Final.jar
  • [2] - hibernación-jpa-2.1-api-1.0.0.Final.jar
Tal como lo hicimos nosotros: en la vista "Proyecto" en "Bibliotecas externas" encontramos el archivo jar requerido y hacemos clic en Ctrl+Shift+C. Ahora creemos y ejecutemos el siguiente comando: javac -cp [1];[2] org\hibernate\query\internal\NativeQueryImpl.java Como resultado, aparecerán nuevos archivos de clase junto al archivo java, que deben actualizarse en el archivo jar:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 8
Hurra, ahora puedes realizar la actualización del jar. Podemos guiarnos por los materiales oficiales : jar uf hibernate-core-5.2.17.Final.jar org\hibernate\query\internal\*.class Open IntelliJ Idea probablemente no le permitirá cambiar archivos. Por lo tanto, antes de realizar la actualización del jar, lo más probable es que tengas que cerrar Idea y, después de la actualización, abrirla. Después de esto, puedes volver a abrir el IDE y ejecutar dubug nuevamente. Los puntos de interrupción no se restablecen entre reinicios del IDE. Por lo tanto, la ejecución del programa se detendrá donde estaba antes. Listo, vemos cómo funcionan nuestros cambios:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 9
Excelente. Pero aquí surge la pregunta: ¿por qué? Simplemente por el hecho de que cuando Gradle construye un proyecto, analiza el bloque de dependencias y repositorios. Gradle tiene un determinado caché de compilación, que se encuentra en una ubicación determinada (consulte "¿ Cómo configurar la ubicación del caché de Gradle? " Si no hay dependencia en el caché, Gradle lo descargará del repositorio. Dado que cambiamos el jar en el caché en sí, entonces Gradle piensa que la biblioteca está en el caché y no bombea nada. Pero cualquier limpieza del caché provocará que nuestros cambios se pierdan. Además, nadie más que nosotros puede ir a buscarlos. ¿Cuánto inconveniente? , ¿no? Qué hacer. Hmm, ¿descargas del repositorio? Entonces necesitamos nuestro repositorio, con preferencias y poetisas. Este es el siguiente paso.

Implementando el repositorio

Existen diferentes soluciones gratuitas para implementar su repositorio: una de ellas es Artifactory y la otra es Apache Archive . Artifactory parece moderno, elegante y moderno, pero tuve dificultades con él, no quería colocar los artefactos correctamente y generé metadatos de Maven erróneos. Por lo tanto, inesperadamente para mí, la versión de Apache funcionó para mí. Resultó no ser tan bonito, pero funciona de forma fiable. En la página de descarga , busque la versión independiente y descomprímala. Tienen su propio " Inicio rápido ". Después del lanzamiento, debe esperar hasta la dirección http://127.0.0.1:8080/#repositorylist. Después de eso, seleccione "Cargar artefacto":
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 10
Haga clic en "Iniciar carga" y luego en "Guardar archivos". Después de esto, aparecerá un mensaje verde de éxito y el artefacto estará disponible en la sección "Examinar". Esto se debe hacer para archivos jar y pom:
Idea IntelliJ: descompilación, compilación, sustitución (o cómo corregir los errores de otras personas) - 11
Esto se debe al hecho de que se especifican dependencias de hibernación adicionales en el archivo pom. Y solo nos queda 1 paso: especificar el repositorio en nuestro script de compilación:

repositories {
    jcenter()
    maven {
        url "http://127.0.0.1:8080/repository/internal/"
    }
}
Y, en consecuencia, la versión de nuestra hibernación será: compile 'org.hibernate:hibernate-core:5.2.17.Final-JAVARUSH'. Eso es todo, ahora nuestro proyecto usa la versión que corregimos y no la original.

Conclusión

Parece que nos conocimos. Espero que haya sido interesante. Estos "trucos" rara vez se realizan, pero si de repente los requisitos de su negocio establecen condiciones que las bibliotecas que utiliza no pueden satisfacer, ya sabe qué hacer. Y sí, aquí tienes un par de ejemplos que se pueden corregir de esta forma:
  • Hay un servidor web llamado Undertow. Hasta hace un tiempo existía un bug que al utilizar un proxy no nos permitía conocer la IP del usuario final.
  • Por el momento, WildFly JPA manejó de cierta manera un momento no tenido en cuenta por la especificación, debido a esto se lanzaron excepciones. Y no era configurable.
#viacheslav
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION