Actualización en caliente de JAVA

introducción

Lea este artículo primero para la reserva de conocimiento: Instrumento JAVA

En este caso, utilizaremos Instrumentel mecanismo para implementar un caso simple de actualización en caliente.

En general, los pasos son los siguientes:

  1. Crear un paquete con premainmétodos jar. Este método detecta regularmente un archivo y realiza actualizaciones activas.
  2. Los parámetros se utilizan cuando se inicia la clase empresarial desde la línea de comandos -javaagent, p java -javaagent:jarpath[=选项] Main.

Hay muchos casos en Internet que usan Mavenempaquetadojar , pero aquí estoy hablando del enfoque de línea de comando puro.

Código

La estructura es la siguiente

inserte la descripción de la imagen aquí
Esta es la estructura compilada, comencemos con la implementación lógica.

1. Escribe una premainclase proxy con métodos

Primero declara una premainclase con métodos. Establecemos el nombre de la clase como Agent: esta clase primero inicia un hilo, y este hilo reemplazará la clase
después de 25 segundos . Entre ellos, leer el archivo en bytes y luego reemplazarlo es un conocimiento relevante.User
Instrument

package com.wyw;

import java.io.File;
import java.io.FileInputStream;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.TimeUnit;

public class Agent {
    
    
    public static void premain(String arg, Instrumentation instrumentation) {
    
    
        System.out.println("premain starting ~");

        Thread thread = new Thread(() -> {
    
    
            try {
    
    
                TimeUnit.SECONDS.sleep(2);
                Class<?> person = Class.forName("com.wyw.User");
                File file = new File("C:\\Users\\QTZ\\IdeaProjects\\hotfix\\src\\com\\wyw\\User.class");
                FileInputStream fileInputStream = new FileInputStream(file);
                byte[] bytes = fileInputStream.readAllBytes();
                instrumentation.redefineClasses(new ClassDefinition(person, bytes));
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        });
        thread.start();
    }
}

2. Escribe clase ejecutiva

Clase de inicio principal:
declare una clase con nombre wywy Userluego imprima las propiedades de esta clase dentro de los 50 segundos.

package com.wyw;

import java.util.concurrent.TimeUnit;

public class Main {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        User user = new User("wyw");
        int i = 0;
        while (i < 50) {
    
    
            TimeUnit.SECONDS.sleep(1);
            System.out.println(i++ + ": " + user.getName());
        }
    }
}

La clase de usuario se define de la siguiente manera:

package com.wyw;

public class User {
    
    
    private static final String DEFAULT_PREFIX = "User: ";

    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return DEFAULT_PREFIX + name;
    }
}

Crear jarpaquete, compilar clase

Primero necesitamos javaccompilar la implementación anterior con:

PS C:\Users\QTZ\IdeaProjects\hotfix\src> javac .\com\wyw\User.java .\com\wyw\Agent.java .\com\wyw\Main.java

Después de la ejecución, puede ver que se generan varios archivos class.

Luego, para crear este jarpaquete, debe declarar una lista para empaquetar, en la que debe especificar Premain-Classla ruta, esta lista se llama MANIFEST.MF:

Manifest-Version: 1.0
Premain-Class: com.wyw.Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

Luego en la línea de comando:

PS C:\Users\QTZ\IdeaProjects\hotfix\src> jar -c -f agent.jar -m .\com\wyw\MANIFEST.MF

Se generará un después de la ejecución agent.jar.

Después de estos dos pasos básicos, comenzamos la clase:

PS C:\Users\QTZ\IdeaProjects\hotfix\src> java -javaagent:agent.jar Main

La consola entonces producirá una salida similar a esta:

PS C:\Users\QTZ\IdeaProjects\hotfix\src> java -javaagent:agent.jar com.wyw.Main
premain starting ~
0: User: wyw
1: User: wyw
2: User: wyw
3: User: wyw
4: User: wyw

En la lógica del código del caso, la clase se reemplazará después de 25 segundos, lo que significa que debemos modificar Userla clase y compilarla durante el período.
Por ejemplo, Userlo modificaremos a:

package com.wyw;

public class User {
    
    
    private static final String DEFAULT_PREFIX = "!!!User: ";

    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return DEFAULT_PREFIX + name;
    }
}

Luego compílalo:

PS C:\Users\QTZ\IdeaProjects\hotfix\src> javac .\com\wyw\User.java

Puede ver la salida de la consola como:

17: User: wyw
18: User: wyw
19: User: wyw
20: User: wyw
21: User: wyw
22: User: wyw
23: User: wyw
24: !!!User: wyw
25: !!!User: wyw
26: !!!User: wyw
27: !!!User: wyw
28: !!!User: wyw
29: !!!User: wyw

Es decir, el reemplazo es exitoso.

Tenga en cuenta que no podemos agregar o eliminar un método a través de la actualización en caliente

Reemplace el original Usercon el siguiente método:

package com.wyw;

public class User {
    
    
    private static final String DEFAULT_PREFIX = "User: ";

    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return addMethod() + DEFAULT_PREFIX + name;
    }

    private String addMethod() {
    
    
        return "Add: ";
    }
}

Después de volver a compilar la clase, se informará el siguiente error al ejecutar:

20: User: wyw
21: User: wyw
22: User: wyw
23: User: wyw
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
        at java.instrument/sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
        at java.instrument/sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:193)
        at com.wyw.Agent.lambda$premain$0(Agent.java:28)
        at java.base/java.lang.Thread.run(Thread.java:834)
24: User: wyw
25: User: wyw

Resumir

Por lo general, no podemos agregar o eliminar un método a través de una actualización activa, pero podemos modificar la implementación interna del método.
El caso simplemente muestra la lógica del código relevante, y el tiempo fijo y las clases fijas se utilizan para la actualización y el reemplazo en caliente.
Proyectado en un proyecto específico, necesitamos implementar nuestra lógica de acuerdo con las condiciones locales de acuerdo con esta lógica, o formular algún tipo de especificación.

Aquí hay algunos más de mis pensamientos.

cuando comprobar

Aquí verifico a intervalos regulares, pero en ingeniería, debe modificarse para verificar a intervalos regulares.

Determinar qué clases necesitan ser actualizadas

Aquí escribo directamente a la muerte. Otros enfoques incluyen:

  • Utilice un archivo para almacenar las rutas que deben actualizarse en caliente y lea este archivo.
  • Almacena la hora de modificación de la clase actual y actualízala cuando cambie la hora.

Supongo que te gusta

Origin blog.csdn.net/sayWhat_sayHello/article/details/121083133
Recomendado
Clasificación