web-dev-qa-db-fra.com

Comment créer une annotation personnalisée avec du code derrière

Je voudrais créer ma propre annotation personnalisée. Mon framework est autonome Java. Lorsque quelqu'un annote sa classe pojo, un code "caché" derrière déclenchera des méthodes).

Par exemple, aujourd'hui dans Java EE nous avons l'annotation @MessageDriven. Et quand vous annotez votre classe avec @MessageDriven Et en plus implémentez l'interface MessageListener il y a un code derrière qui déclenchera onMessage(Message msg). lorsqu'un message arrive d'une file d'attente/rubrique.

Comment créer une annotation (@MyMessageDriven) Qui pourrait être ajoutée à un pojo et implémenter également MyCustomMessageListener.

Le résultat que je souhaite est un déclencheur de code "caché" (le mien) qui déclenchera une méthode d'une interface implémentée (exactement comme cela fonctionne avec l'exemple que j'ai écrit ci-dessous).

36
rayman

Je recommande de lire cette entrée de blog (instantané sur archive.org) jusqu'au point où l'auteur se souvient qu'il a accès à la fonction d'analyse des composants de Spring.

Le problème initial consiste à analyser le chemin d'accès aux classes pour trouver des classes avec l'annotation personnalisée. Une fois cela fait, vous avez les objets dans votre application autonome à travers lesquels en utilisant object.getClass().getAnnotations() , vous pouvez ensuite injecter les écouteurs ou le comportement personnalisé que vous devez ajouter aux objets contenant la coutume annotations.

Supposons que vous ayez l'annotation personnalisée suivante:

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMessageDriven {}

Et vous l'utilisez une classe dans votre application:

@MyMessageDriven
public class MyObject {}

Maintenant, à l'emplacement approprié dans votre application, vous devriez avoir une méthode pour distribuer toutes les classes portant MyMessageDriven:

Set<Class<?>> findAllMessageDrivenClasses() {
  final StopWatch sw = new StopWatch();
  sw.start();
  final Reflections reflections = new Reflections("org.projectx", new TypeAnnotationsScanner());
  Set<Class<?>> allMessageDrivens = reflections.getTypesAnnotatedWith(MyMessageDriven.class); // NOTE HERE
  sw.stop();
  return allMessageDrivens;
}

Ayant cela, je suppose qu'il y a un point dans votre application que (1) vous avez accès aux objets dans votre application, ou (2) il existe un modèle de visiteur ou d'itérateur sur tous les objets de l'application. Donc, à un moment donné, je suppose que nous avons tous les objets ciblés comme objects:

Set<Class<?>> msgDrivenClasses = findAllMessageDrivenClasses();
for (Object o : objects) {
  if (msgDrivenClasses.contains(o.getClass()) {
    invokeTheMessageListener(o);
  }
}

D'un autre côté, il devrait y avoir une implémentation de MyMessageListener qui est disponible lorsque les objets ayant MyMessageDriven sont trouvés:

void invokeTheMessageListener(Object o) {
  theMessageListener.onMessage(o);
}

Cette réponse est adaptée de l'entrée de blog, veuillez donc vous référer au blog pour la configuration des bibliothèques. Et, dernier point mais non le moindre, il s'agit d'un exemple de code pour le problème et il peut être refactorisé pour un style plus compatible avec les modèles et élégant.

Mise à jour : Il est nécessaire que les objets ciblés soient conscients de leurs propres écouteurs. Je suggère donc l'approche suivante. Ayons une interface MyMessageListenerAware:

interface MyMessageListenerAware {
  MyMessageListener getMyMessageListener();
}

// and this is the original MyMessageListener
interface MyMessageListener {
  void onMessage(Object o);
}

Maintenant, les objets cibles doivent implémenter l'interface ci-dessus:

class MySampleObject implements MyMessageListenerAware {

  public MyMesssageListener getMyMessageLisener() {
    return mySampleObjectImplementationOfMyMessageListener;
  }

}

Ayant cela, la méthode invokeTheMessageListener devient comme:

void invokeMessageListener(Object o) {
  if (o instance MyMessageListenerAware) {
    MyMessageListener l = ((MyMessageListenerAware) o).getMyMessageListener();
    l.onMessage(o);
  }
}

Bien que , je recommande fortement de lire sur le modèle Visitor ou Strategy . Ce que vous visez à faire me semble comme si vous aviez besoin que certains objets réagissent/agissent/traitent à un objet/événement commun dans l'application mais chacun avec leur propre interprétation/algorithme/implémentation.

35
nobeh

créer une annotation quelque chose comme ceci:

 public @interface MyMessageDriven{
 }

Et vous avez une interface qui peut appliquer des annotations comme celle-ci:

public interface MyMessagListener {

    public void message();
}



@MyMessageDriven  
public class MyMessage implements MyMessagListener  {
   public void message(){
     System.out.println(" I am executed")
   }
} 

Chargez la classe ci-dessus à l'aide du chargeur de classe et à l'aide de réflexions, vérifiez que l'annotation est présente.

s'il est présent, utilisez une instance chargée pour l'exécuter.

  Object obj = ClassLoader.getSystemClassLoader().loadClass("MyMessage").newInstance();
  MyMessagListener mml =  (MyMessagListener) obj;
  mml.message();

Implémentation d'écouteur que vous pouvez placer dans la classe MyMessage ou une autre classe qui implémente MessageListener.

Dans ce cas, vous devez fournir l'implémentation de message() ce qu'elle va faire.

Mais cette classe doit être chargée et le plus important ici est de savoir comment votre classe MyMessage est chargée.

Cela est basé sur les métadonnées présentes dans la classe MyMessage. De la même manière, dans le scénario en temps réel, c'est ainsi que cela fonctionne.

L'annotation est une métadonnée à une classe qui dit en fonction des données fournies, faites quelque chose. Si ces métadonnées ne sont pas présentes dans la classe MyMessage, vous n'avez pas besoin d'exécuter la méthode message().

J'espère que ceci vous aidera.

10
UVM