reflection use - Una classe Java può aggiungere un metodo a se stesso in fase di runtime?




what is (9)

Una classe può aggiungere un metodo a se stesso in fase di runtime (come da un blocco static ), in modo che se qualcuno sta eseguendo il reflection su questa classe, vedrà il nuovo metodo, anche se non è stato definito in fase di compilazione?

Sfondo:

Un framework che sto usando si aspetta che le classi Action siano definite che hanno un metodo doAction(...) , per convenzione. Il framework controlla queste classi in fase di esecuzione per vedere quale tipo di parametri sono disponibili nel loro metodo doAction() . Ad esempio: doAction ( String a, Integer b)

Mi piacerebbe che ogni classe fosse in grado di generare in modo programmatico il suo metodo doAction() con vari parametri, just-in-time quando viene ispezionato. Il corpo del metodo può essere vuoto.


Answers

Non ho mai provato niente del genere, ma dovresti dare un'occhiata a ASM , cglib e Javassist .


No, non è (facilmente) possibile in Java.

Sembra che tu stia cercando di usare Java come se fosse un linguaggio di programmazione dinamico. Ad esempio, Ruby ha classi aperte: puoi aggiungere e rimuovere metodi dalle classi Ruby in fase di runtime. In Ruby, puoi anche avere un metodo "metodo mancante" nella tua classe, che verrà chiamato quando proverai a chiamare un metodo che non esiste nella classe. Anche una cosa del genere non esiste in Java.

C'è una versione di Ruby che gira su JVM, JRuby, e deve fare trucchi molto difficili per far funzionare le classi aperte sulla JVM.


Sembra che non ci sia modo di aggiungere il metodo in modo dinamico. Ma puoi preparare una classe con un elenco di metodi o un hash come:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

public class GenericClass {
    private HashMap<String, Method> methodMap = new HashMap<String, Method>();

    public Object call(String methodName,Object ...args) 
               throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method = methodMap.get(methodName);
        return method.invoke(null, args);
    }

    public void add(String name,Method method){
        if(Modifier.isStatic(method.getModifiers()))
            methodMap.put(name, method);
    }

    public static void main(String[] args) {
        try   {
            GenericClass task = new GenericClass();
            task.add("Name",Object.class.getMethod("Name", new Class<?>[0]));
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
   }
}

Quindi, utilizzando i riflessi è possibile impostare o annullare l'attributo.


Andres_D ha ragione, possiamo farlo molto bene usando il caricamento di classi personalizzate, ecco una guida dettagliata su come fare questo: http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1

L'articolo spiega come scrivere codice Java dinamico. Descrive la compilazione del codice sorgente del runtime, il ricaricamento della classe e l'uso del modello di progettazione Proxy per apportare modifiche a una classe dinamica trasparente al suo chiamante.

In realtà, in Austria il ricercatore ha scritto una JVM che consente persino di ricaricare classi con gerarchie di tipi diversi. Hanno ottenuto questo risultato utilizzando i punti di salvataggio del thread esistenti per generare un "side side" completo di un oggetto e tutti i relativi riferimenti e contenuti referenziati e quindi, una volta completamente rimescolato con tutte le modifiche richieste, è sufficiente scambiare tutte le classi modificate. [1] Qui un link al loro progetto http://ssw.jku.at/dcevm/ la sponsorizzazione dell'oracolo rende certamente interessanti le speculazioni sui piani futuri.

Le modifiche meno invasive ai corpi e ai campi del metodo sono già possibili nella java VM standard che utilizza le funzionalità di Hot Swap della JPDA, come introdotto in Java 1.4:
docs.oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap

Non sono sicuro se sia stato il primo, ma questo documento del 2001 di Sun sembra essere una delle prime proposte che menzionano le funzionalità di HotSpot a Hot Swap. [2]

RIFERIMENTO

[1] T. Würthinger, C. Wimmer e L. Stadler, "Evoluzione dinamica del codice per Java", presentato all'ottava conferenza internazionale sui principi e la pratica della programmazione a Java, Vienna, 2010.

[2] M. Dmitriev, "Verso una tecnologia flessibile e sicura per l'evoluzione runtime delle applicazioni di linguaggio Java", nel laboratorio OOPSLA su Engineering Complex Object-Oriented Systems for Evolution, 2001.


Proxy può aiutare. Ma devi istanziare un Proxy ogni volta che vuoi aggiungere o rimuovere un metodo.


Non è semplice Una volta che una classe viene caricata da un classloader, non c'è modo di cambiare i metodi delle classi caricate. Quando viene richiesta una classe, un programma di caricamento classi lo caricherà e lo collegherà . E non c'è modo (con Java) di cambiare il codice collegato o aggiungere / rimuovere metodi.

L'unico trucco che mi viene in mente è giocare con i classloader. Se elimini un classloader personalizzato, le classi caricate da quel classloader dovrebbero essere eliminate o inaccessibili. L'idea che mi viene in mente è

  1. implementa un classloader personalizzato
  2. carica la classe dinamica con quel classloader personalizzato
  3. se abbiamo una versione aggiornata di questa classe,
  4. rimuovere il classloader personalizzato e
  5. carica la nuova versione di questa classe con una nuova istanza del classloader personalizzato

Lo lascio come spunto di riflessione , non posso provarlo, se questo porta a una soluzione o se abbiamo delle insidie.

Come una semplice risposta alla domanda: No, non possiamo cambiare una classe caricata come se fosse possibile modificare il contenuto dei campi con la riflessione. (non possiamo aggiungere o rimuovere campi).


Non sono sicuro che sia possibile. Tuttavia, potresti usare AspectJ, ASM, ecc. E intrecciare questi metodi nelle classi appropriate.

L'altra alternativa è usare la composizione per avvolgere la classe di destinazione e fornire il metodo doAction. In questo caso finirai per delegare alla classe di destinazione.


Puoi avere un metodo doAction che fa ciò che vorresti fosse il metodo generato. C'è una ragione per cui deve essere generata o può essere dinamica?


Method method = someVariable.class.getMethod(SomeClass);
String status = (String) method.invoke(method);

SomeClass è la classe e someVariable è una variabile.





java reflection code-generation javassist