web-dev-qa-db-fra.com

Une classe Java classe peut-elle s'ajouter une méthode à l'exécution)?

Une classe peut-elle s'ajouter une méthode à l'exécution (comme à partir d'un bloc static), de sorte que si quelqu'un effectue une réflexion sur cette classe, il verra la nouvelle méthode, même si elle n'a pas été définie à compiler le temps?

Contexte:

Un framework que j'utilise attend que les classes Action soient définies qui ont une méthode doAction(...), par convention. Le framework inspecte ces classes au moment de l'exécution pour voir quels types de paramètres sont disponibles dans leur méthode doAction(). Par exemple: doAction (String a, Integer b)

J'aimerais que chaque classe puisse générer par programmation sa méthode doAction() avec divers paramètres, juste à temps lorsqu'elle est inspectée. Le corps de la méthode peut être vide.

56
slattery

Ce n'est pas simple. Une fois qu'une classe est chargée par un chargeur de classe, il n'y a aucun moyen de changer les méthodes des classes chargées. Lorsqu'une classe est demandée, un chargeur de classe la charge et link it. Et il n'y a aucun moyen (avec Java) de changer le code lié ou d'ajouter/supprimer des méthodes.

La seule astuce qui me vient à l'esprit est de jouer avec les chargeurs de classe. Si nous supprimons un chargeur de classe personnalisé, les classes chargées par ce chargeur de classe doivent également être supprimées ou inaccessibles. L'idée qui me vient à l'esprit est de

  1. implémenter un chargeur de classe personnalisé
  2. charger la classe dynamic avec ce chargeur de classe personnalisé
  3. si nous avons une version mise à jour de cette classe,
  4. supprimer le chargeur de classe personnalisé et
  5. charger la nouvelle version de cette classe avec une nouvelle instance du chargeur de classe personnalisé

Je laisse cela comme matière à réflexion, ne peut pas prouver, si cela conduit à une solution ou si nous avons des pièges.

Comme réponse simple à la question: Non, nous ne pouvons pas changer une classe chargée comme nous pouvons changer le contenu des champs avec réflexion. (nous ne pouvons pas ajouter ou supprimer des champs également).

54
Andreas_D

Andres_D a raison, nous pouvons très bien le faire en utilisant le chargement de classe personnalisé, voici un guide détaillé sur la façon de le faire: http://www.javaworld.com/javaworld/jw-06-2006/jw- 0612-dynamic.html? Page = 1

L'article explique comment écrire du code dynamique Java. Il explique la compilation du code source au moment de l'exécution, le rechargement de classe et l'utilisation du modèle de conception Proxy pour apporter des modifications à une classe dynamique transparente pour son appelant.

En fait, un chercheur en Autriche a écrit une machine virtuelle Java qui permet même de recharger des classes avec différentes hiérarchies de types. Ils ont atteint cet objectif en utilisant des points de sauvegarde de threads existants pour générer un `` univers latéral '' complet d'un objet et de toutes ses références associées et de son contenu référencé, puis une fois entièrement réorganisé avec toutes les modifications requises, il suffit de permuter dans toutes les classes modifiées. [1] Voici un lien vers leur projet http://ssw.jku.at/dcevm/ le parrainage d'Oracle fait certainement des spéculations intéressantes sur les plans futurs.

Des modifications moins intrusives des corps de méthode et des champs sont déjà possibles dans la norme Java VM utilisant les capacités de remplacement à chaud du JPDA comme présenté dans Java 1.4:
docs.Oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap

Je ne sais pas s'il s'agissait du premier mais le document de 2001 de cet employé de Sun semble être l'une des premières propositions mentionnant les capacités du HotSpot to Hot Swap. [2]

[~ # ~] référence [~ # ~]

[1] T. Würthinger, C. Wimmer et L. Stadler, "Dynamic Code Evolution for Java", présenté à la 8e Conférence internationale sur les principes et la pratique de la programmation à Java, Vienne, 2010.

[2] M. Dmitriev, "Towards flexible and safe technology for runtime evolution of Java language applications", in OOPSLA Workshop on Engineering Complex Object-Oriented Systems for Evolution, 2001.

22
DrGranit

Je n'ai jamais rien essayé de tel, mais vous devriez jeter un œil à ASM , cglib , et Javassist .

8
Ryan Stewart

Non, ce n'est pas (facilement) possible en Java.

Il semble que vous essayez d'utiliser Java comme s'il s'agissait d'un langage de programmation dynamique. Par exemple, Ruby a des classes ouvertes: vous pouvez ajouter et supprimer des méthodes from Ruby classes à l'exécution. Dans Ruby, vous pouvez également avoir une méthode "méthode manquante" dans votre classe, qui sera appelée lorsque vous essayez d'appeler une méthode qui n'existe pas dans le Une telle chose n'existe pas non plus en Java.

Il existe une version de Ruby qui s'exécute sur la JVM, JRuby, et il doit faire des tours très difficiles pour faire fonctionner les classes ouvertes sur la JVM.

4
Jesper

Vous pouvez avoir une méthode doAction qui fait tout ce que vous voudriez que la méthode générée fasse. Y a-t-il une raison pour laquelle il doit être généré ou peut-il être dynamique?

2
Peter Lawrey

Le proxy peut aider. Mais vous devez instancier un proxy chaque fois que vous souhaitez ajouter ou supprimer une méthode.

1
Boki

Ce que je suggère devrait fonctionner pour votre situation: 1. Vous avez une classe MyClass existante avec n méthodes 2. Vous voulez inclure la méthode (n + 1) qui n'est pas dans la classe lors de la compilation dans un autre fichier source .Java

Ma façon de le résoudre est l'héritage. Créez un nouveau fichier source .Java pour une classe MyClassPlusOne étendant la première classe MyClass. Compilez cette classe et utilisez l'objet. Comment puis-je compiler et déployer une Java lors de l'exécution?

class MyClassPlusOne extends MyClass
{
     void doAction(String a, Integer b)
     {
         int myNPlus1 = a+b;
         //add whatever you want before compiling this code
      }
}
1
user9721802

Je crois que vous avez besoin d'un outil/cadre de modification de code octet, comme asm, cglib ou javassist. Vous pouvez y parvenir via des aspects/tissages comme c'est le cas au printemps, mais je crois que vous devez encore définir la méthode en premier.

1
carlspring

Il semble qu'il n'y ait aucun moyen d'ajouter dynamiquement une méthode. Mais vous pouvez préparer une classe avec une liste de méthodes ou un hachage comme:

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

Ensuite, à l'aide de réflexions, vous pouvez définir ou désactiver l'attribut.

Je ne suis pas sûr que ce soit possible. Cependant, vous pouvez utiliser AspectJ, ASM, etc. et intégrer ces méthodes dans les classes appropriées.

L'autre alternative consiste à utiliser la composition pour encapsuler la classe cible et fournir la méthode doAction. Vous finiriez par déléguer à la classe cible dans ce cas.

0
tjg184