2013-01-14 14 views
12

Dire che ho una classe denominata NameGenerator. Posso usarlo per generare nomi in base a una determinata logica. Quindi scrivo una classe TestNameGeneration con un metodo che richiede una lettera all'utente e genera un nome in accordo. Ora voglio cambiare la logica nella classe NameGeneration e applicare quella particolare modifica senza arrestare l'applicazione.Come sostituire le classi in un'applicazione in esecuzione in java?

Ho fatto questo per saperne di più sui programmi di caricamento di classe e qualcuno può spiegare i concetti chiave che devo imparare a fare qualcosa del genere o inserire riferimenti?

risposta

18

Ecco un test di funzionamento. Ogni 5 secondi Test.main() ricarica test.Test1.classe dal file system e chiama Test1.hello()

package test; 

public class Test1 { 
    public void hello() { 
     System.out.println("Hello !"); 
    } 
} 

public class Test { 

    static class TestClassLoader extends ClassLoader { 
     @Override 
     public Class<?> loadClass(String name) throws ClassNotFoundException { 
      if (name.equals("test.Test1")) { 
       try { 
        InputStream is = Test.class.getClassLoader().getResourceAsStream("test/Test1.class"); 
        byte[] buf = new byte[10000]; 
        int len = is.read(buf); 
        return defineClass(name, buf, 0, len); 
       } catch (IOException e) { 
        throw new ClassNotFoundException("", e); 
       } 
      } 
      return getParent().loadClass(name); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     for (;;) { 
      Class cls = new TestClassLoader().loadClass("test.Test1"); 
      Object obj = cls.newInstance(); 
      cls.getMethod("hello").invoke(obj); 
      Thread.sleep(5000); 
     } 
    } 
} 

Eseguirlo. Quindi modificare e ricompilare Test1

System.out.println("Hello !!!"); 

mentre Test è in esecuzione. Vedrete uscita Test1.hello cambiato

... 
Hello ! 
Hello ! 
Hello !!! 
Hello !!! 

Ecco come ad esempio Tomcat ricarica webapps. Ha un ClassLoader separato per ogni webapp e carica una nuova versione in un nuovo ClassLoader. Quello vecchio è GCed proprio come qualsiasi oggetto Java e anche le vecchie classi.

Nota che abbiamo caricato Test1 con TestClassLoader e invocato il suo primo metodo con reflection. Ma tutte le dipendenze Test1 verranno caricate implicitamente con il caricatore di classe Test1, ovvero tutte le applicazioni Test1 verranno caricate da JVM in TestClassLoader.

+0

Grazie per l'aiuto. Avere una nuova istanza del caricatore di classe per ogni nuova classe di caricamento, come hai fatto qui risolto il problema. –

0

Che dire di uno strategy pattern? Potrebbe essere una soluzione migliore per il tuo problema invece di usare classloader.

+1

Come potrebbe aiutarlo a caricare dinamicamente la versione modificata delle classi? – Andremoniy

+0

Siccome so che lo schema a stella potrebbe aiutare a scegliere tra diverse implementazioni come preferiamo in fase di esecuzione codificando su un'interfaccia. Ma non possiamo introdurre una nuova implementazione al volo. –

+0

@Prasad è possibile iniettare al volo: vedere il mio esempio sopra con il modello di strategia e DI: setNameGeneration (Class.forName ("com.bean.ClasseA"). NewInstance()) –

1

Ci sono 2 modi:

  1. per sovrascrivere il caricatore di classe si stai usando prima usando il classloader esistente per eseguire il bootstrap della tua applicazione e in particolare per la classe che devi avere un aggiornamento dinamico, devi usare il classloader sovrascritto.
  2. Per utilizzare il framework OSGi. A seconda della scala della tua applicazione, il framework OSGi potrebbe non essere una buona scelta in quanto richiede di seguire la sua convenzione di codifica.

Speranza che aiuta

+0

+1 per OSGi. Supporta l'aggiunta/rimozione di bundle in fase di runtime out-of-the-box. – Puce

+0

Grazie per il primo suggerimento. Ma non voglio usare OSGI come l'unica intenzione di questa piccola applicazione è l'apprendimento dei concetti core di caricamento della classe java. –

1

E 'molto semplice, si utilizzerà di vantaggio di una programmazione orientata agli oggetti e di usare l'interfaccia, non è necessario per gestire classloader È possibile iniettare l'implementazione con un setter:

creare un'interfaccia chiamata NameGeneration Crea implementazione n NameGenerationImpl1, NameGenerationimpl2 per esempio Nella classe cliente definire una variabile:

NameGeneration nameGeneration ; 

e il setter:

public void setNameGeneration(NameGeneration nameGeneration) { 
this.nameGeneration = nameGeneration ; 
} 

nameGeneration genererà ciò che si desidera.

Quando si cambia l'algoritmo si cambia l'attuazione facendo per esempio:

setNameGeneration(new NameGenerationImpl1()) ; 

o

setNameGeneration(new NameGenerationImpl2()) ; 
+0

Che cosa significa 'POO'? Non capisco –

+0

@Mohammad: Penso che voglia dire OOP ... –

+0

Questo è fondamentalmente il modello di strategia. –

1

Scrivi la tua classloader:

Ho avuto un successo con this uno come una base Si dovrebbe anche guardare al tutorial this.

+0

I collegamenti sono interrotti. Qualche idea su come trovare questi articoli? –

0

È possibile scrivere il proprio classloader personalizzato. Quando c'è un cambiamento nel file di classe o nella risorsa/jar contenente il file di classe (controlla il timestamp), distruggi l'istanza del classloader precedente e crea una nuova istanza del classloader che a sua volta caricherà il nuovo file di classe.

Problemi correlati