2017-09-27 4 views
0

Pour des raisons de performances, j'ai une classe qui stocke un Map dont la clé est Class<?> et sa valeur est fonction des champs de cette classe. La carte est remplie lors de l'exécution du code en fonction du type de l'objet appelant. Ce qui précède est une généralisation/simplificationÉcoute de la classe recharger en Java

public class Cache { 

    private static final Map<Class<?>, String> fieldsList = ...; 


    //Synchronization omitted for brevity 
    public String getHqlFor(Class<?> entity){ 
     if (!fieldsList.containsKey(entity)) 
      fieldsList.put(entity,createHql(entity)); 
     return fieldsList.get(entity); 
    } 

} 

Au cours du développement, grâce à l'aide de JRebel, je fais souvent des modifications aux classes en changeant les propriétés entières ou tout simplement leur nom. Je peux continuer le développement très bien. Cependant, si je mets déjà une valeur dans le cache, il sera périmé pour toujours.

Ce que je demande ici est s'il est possible d'intercepter l'événement qu'une classe dans le classpath a changé. Très large ... Mais mon problème spécifique est très simple: puisque j'ai un tel besoin seulement pendant le développement, je veux juste effacer ce cache dans le cas n'importe quelle classe dans mes changements de classpath.

Comment puis-je accomplir ceci? Je ne ai pas besoin de faire quoi que ce soit spécial que l'interception de l'événement et simplement essuyer le cache

+0

Vous pourriez utiliser un Classloader personnalisé qui fait cela? – daniu

Répondre

0

Il y a une classe existante ClassValue qui fait déjà le travail pour vous:

public class Cache { 

    private final ClassValue<String> backend = new ClassValue<String>() { 
     @Override 
     protected String computeValue(Class<?> entity) { 
      return createHql(entity); 
     } 
    }; 

    public String getHqlFor(Class<?> entity){ 
     return backend.get(entity); 
    } 
} 

Lorsque vous appelez get, il appelle computeValue s'il s'agit du premier appel de cet argument spécifique Class ou renvoie la valeur déjà existante dans le cas contraire. Il se soucie déjà de la sécurité des threads et permet aux classes d'obtenir des déchets collectés. Vous n'avez pas besoin de savoir quand le déchargement de classe se produit réellement.

+0

Cela semble bon, mais pas applicable à mon cas. J'ai beaucoup simplifié mon exemple et le getHqlFor prend au moins deux classes en params. Mais je cherche comment l'adapter.Merci –

+0

Cela semble applicable à mon cas maintenant :-) –

+0

Cela ne fonctionnera pas dans le cas des rechargements de classe JRebel car la nouvelle version de la classe se comparera à la précédente afin qu'elle ne calcule pas une nouvelle valeur. Vous pouvez essayer de recharger l'exemple de code ici en décommentant le nouveau champ et en voyant l'ancien résultat: https://pastebin.com/iKhA6RuX – Murka

3

JRebel dispose d'une API de plug-in que vous pouvez utiliser pour déclencher le code sur les rechargements de classe. Le tutoriel complet avec application exemple et plug-in disponible ici: https://manuals.zeroturnaround.com/jrebel/advanced/custom.html

Le plugin JRebel est un pot autonome construit sur le SDK JRebel, qui est attaché à l'application en cours d'exécution par l'argument JVM -Drebel.plugins=/path/to/my-plugin.jar. L'agent JRebel attaché à l'application va charger et démarrer les plugins à partir de cet argument.
Si l'application n'est pas démarrée avec l'agent JRebel, le plugin n'est tout simplement pas chargé.

Dans votre exemple, vous voulez enregistrer un ClassEventListener qui effacera la carte Cache.fieldsList. Comme il est un domaine privé, vous devez y accéder via la réflexion ou ajouter un get/méthode claire via un ClassBytecodeProcessor

public class MyPlugin implements Plugin { 
    void preinit() { 
    ReloaderFactory.getInstance().addClassReloadListener(new ClassEventListenerAdapter(0) { 
     @Override 
     public void onClassEvent(int eventType, Class<?> klass) throws Exception { 
     Cache.clear(); 
     } 
    }); 
    } 
    // ... other methods ... 
} 

Et pour effacer la carte

public class CacheCBP extends JavassistClassBytecodeProcessor { 
    public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) { 
    ctClass.addMethod(CtMethod.make("public static void clear() { fieldsList.clear(); }", ctClass)); 
    } 
} 

Cependant une meilleure option est seulement effacer/recalculer l'entrée de classe unique sur le rechargement de classe si possible. L'exemple n'affichait pas si les informations calculées à partir d'une classe dépendaient des infos de la superclasse, mais si cela est vrai, le SDK JRebel a également des méthodes pour enregistrer un écouteur reload sur la hiérarchie des classes.

+0

Je crois comprendre que cela nécessite le SDK Jrebel dans le classpath. Cela, cependant, ne devrait jamais être déployé en production. Je veux dire que mon patron va se fâcher si je demande de polluer le classpath de l'application avec des dépendances de JRebel. Je vais y regarder de plus près –

+0

Le SDK JRebel est déjà sur le classpath lors de l'utilisation de JRebel. – Murka

+0

Pas lorsque vous compilez en utilisant la commande javac de Ant avec un classpath bien défini. Quoi qu'il en soit, j'ai juste essayé de taper "com.zeroturnaround" dans mon auto complète et rien n'a montré. 'Plugin' ne montre pas non plus les résultats du monde JRebel –