atualização quente de JAVA

introdução

Leia este artigo primeiro para reserva de conhecimento: Instrumento JAVA

Nesse caso, usaremos Instrumento mecanismo para implementar um caso simples de hot update.

No geral, as etapas são as seguintes:

  1. Crie um pacote com premainmétodos jar. Este método detecta regularmente um arquivo e executa atualizações dinâmicas.
  2. Os parâmetros são usados ​​ao iniciar uma classe de negócio a partir da linha de comando -javaagent, por exemplo java -javaagent:jarpath[=选项] Main.

Existem muitos casos na Internet que usam Mavenempacotamentojar , mas aqui estou falando sobre a abordagem de linha de comando pura.

Código

A estrutura é a seguinte

insira a descrição da imagem aqui
Esta é a estrutura compilada, vamos começar com a implementação da lógica.

1. Escreva uma premainclasse de proxy com métodos

Primeiro declare uma premainclasse com métodos. Definimos o nome da classe como Agent: esta classe primeiro inicia um thread e este thread substituirá a classe
após 25 segundos . Entre eles, ler o arquivo em bytes e depois substituí-lo é um conhecimento 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. Escrever aula de negócios

Classe de inicialização principal:
declare uma classe nomeada e wyw, Userem seguida, imprima as propriedades dessa classe em 50s.

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());
        }
    }
}

A classe User é definida da seguinte forma:

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;
    }
}

Criar jarpacote, compilar classe

Primeiro precisamos javaccompilar a implementação acima com:

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

Após a execução, você pode ver que vários arquivos são gerados class.

Então para criar este jarpacote, você precisa declarar uma lista para empacotamento, na qual você precisa especificar Premain-Classo caminho, esta lista é nomeada MANIFEST.MF:

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

Então na linha de comando:

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

Um será gerado após a execução agent.jar.

Após essas duas etapas básicas serem concluídas, iniciamos a aula:

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

O console produzirá uma saída semelhante 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

Na lógica do código do caso, a classe será substituída após 25 segundos, o que significa que precisamos modificar Usera classe e compilá-la durante o período.
Por exemplo, vamos Usermodificá-lo para:

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;
    }
}

Em seguida, compile-o:

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

Você pode ver a saída do console 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

Ou seja, a substituição é bem-sucedida.

Observe que não podemos adicionar ou excluir um método por meio de hot update

Substitua o original Userpelo seguinte 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: ";
    }
}

Após recompilar a classe, o seguinte erro será relatado ao executar:

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

Normalmente, não podemos adicionar ou excluir um método por meio de hot update, mas podemos modificar a implementação interna do método.
O caso simplesmente mostra a lógica de código relevante, e o tempo fixo e as classes fixas são usadas para atualização e substituição a quente.
Projetado em um projeto específico, precisamos implementar nossa lógica de acordo com as condições locais de acordo com essa lógica, ou formular algum tipo de especificação.

Aqui estão mais alguns dos meus pensamentos.

quando verificar

Aqui eu verifico em intervalos regulares, mas na engenharia, deve ser modificado para verificar em intervalos regulares.

Determine quais classes precisam ser atualizadas

Aqui escrevo diretamente para a morte. Outras abordagens incluem:

  • Use um arquivo para armazenar os caminhos que precisam ser atualizados e leia esse arquivo.
  • Armazene o horário de modificação da aula atual e atualize-o quando o horário mudar.

Acho que você gosta

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