Erstellen einer benutzerdefinierten Anmerkung in Java

1. Einleitung

Java-Annotationen sind ein Mechanismus zum Hinzufügen von Metadateninformationen zu unserem Quellcode. Sie sind ein leistungsfähiger Bestandteil von Java und wurden in JDK5 hinzugefügt. Anmerkungen bieten eine Alternative zur Verwendung von XML-Deskriptoren und Markierungsschnittstellen.

Obwohl wir sie an Pakete, Klassen, Schnittstellen, Methoden und Felder anhängen können, haben Anmerkungen für sich genommen keine Auswirkungen auf die Ausführung eines Programms.

In diesem Tutorial konzentrieren wir uns darauf, wie Sie benutzerdefinierte Anmerkungen erstellen und verarbeiten. Weitere Informationen zu Anmerkungen finden Sie in unserem Artikel zu den Grundlagen von Anmerkungen.

2. Benutzerdefinierte Anmerkungen erstellen

Wir werden drei benutzerdefinierte Anmerkungen erstellen, um ein Objekt in eine JSON-Zeichenfolge zu serialisieren.

Wir werden den ersten auf Klassenebene verwenden, um dem Compiler anzuzeigen, dass unser Objekt serialisiert werden kann. Als Nächstes wenden wir das zweite auf die Felder an, die in die JSON-Zeichenfolge aufgenommen werden sollen.

Schließlich verwenden wir die dritte Annotation auf Methodenebene, um die Methode anzugeben, mit der wir unser Objekt initialisieren.

2.1. Beispiel für Annotationen auf Klassenebene

Der erste Schritt zum Erstellen einer benutzerdefinierten Anmerkung besteht darin, sie mit dem Schlüsselwort @interface zu deklarieren:

public @interface JsonSerializable { }

Der nächste Schritt ist das Hinzufügen von Meta-Annotationen, um den Umfang und das Ziel unserer benutzerdefinierten Annotation anzugeben :

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.Type) public @interface JsonSerializable { }

Wie wir sehen können, ist unsere erste Annotation zur Laufzeit sichtbar und kann auf Typen (Klassen) angewendet werden . Darüber hinaus verfügt es über keine Methoden und dient daher als einfache Markierung zum Markieren von Klassen, die in JSON serialisiert werden können.

2.2. Beispiel für Anmerkungen auf Feldebene

Auf die gleiche Weise erstellen wir unsere zweite Anmerkung, um die Felder zu markieren, die in den generierten JSON aufgenommen werden sollen:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface JsonElement { public String key() default ""; }

Die Annotation deklariert einen String-Parameter mit dem Namen "key" und einem leeren String als Standardwert.

Wenn Sie benutzerdefinierte Anmerkungen mit Methoden erstellen, sollten Sie sich bewusst sein, dass diese Methoden keine Parameter haben dürfen und keine Ausnahme auslösen können . Außerdem sind die Rückgabetypen auf Grundelemente, Zeichenfolgen, Klassen, Aufzählungen, Anmerkungen und Arrays dieser Typen beschränkt, und der Standardwert darf nicht null sein .

2.3. Annotationsbeispiel auf Methodenebene

Stellen wir uns vor, dass wir vor dem Serialisieren eines Objekts in eine JSON-Zeichenfolge eine Methode zum Initialisieren eines Objekts ausführen möchten. Aus diesem Grund erstellen wir eine Anmerkung, um diese Methode zu markieren:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Init { }

Wir haben eine öffentliche Annotation mit Laufzeitsichtbarkeit deklariert, die wir auf die Methoden unserer Klassen anwenden können.

2.4. Anwenden von Anmerkungen

Nun wollen wir sehen, wie wir unsere benutzerdefinierten Anmerkungen verwenden können. Stellen wir uns zum Beispiel vor, wir haben ein Objekt vom Typ Person , das wir in eine JSON-Zeichenfolge serialisieren möchten. Dieser Typ verfügt über eine Methode, bei der der erste Buchstabe des Vor- und Nachnamens groß geschrieben wird. Wir möchten diese Methode aufrufen, bevor wir das Objekt serialisieren:

@JsonSerializable public class Person { @JsonElement private String firstName; @JsonElement private String lastName; @JsonElement(key = "personAge") private String age; private String address; @Init private void initNames() { this.firstName = this.firstName.substring(0, 1).toUpperCase() + this.firstName.substring(1); this.lastName = this.lastName.substring(0, 1).toUpperCase() + this.lastName.substring(1); } // Standard getters and setters }

Durch die Verwendung unserer benutzerdefinierten Anmerkungen geben wir an, dass wir ein Personenobjekt in eine JSON-Zeichenfolge serialisieren können . Darüber hinaus sollte die Ausgabe nur die Felder Vorname , Nachname und Alter dieses Objekts enthalten. Darüber hinaus möchten wir, dass die Methode initNames () vor der Serialisierung aufgerufen wird.

Indem Sie den Schlüsselparameter der Annotation @JsonElement auf "personAge" setzen, geben wir an, dass wir diesen Namen als Bezeichner für das Feld in der JSON-Ausgabe verwenden.

Zur Demonstration haben wir initNames () privat gemacht, sodass wir unser Objekt nicht durch manuelles Aufrufen initialisieren können und unsere Konstruktoren es auch nicht verwenden.

3. Verarbeiten von Anmerkungen

Bisher haben wir gesehen, wie benutzerdefinierte Anmerkungen erstellt und zum Dekorieren der Person- Klasse verwendet werden. Jetzt werden wir sehen, wie Sie sie mithilfe der Reflection-API von Java nutzen können.

Der erste Schritt besteht darin, zu überprüfen, ob unser Objekt null ist oder nicht und ob sein Typ die Annotation @JsonSerializable enthält oder nicht:

private void checkIfSerializable(Object object) { if (Objects.isNull(object)) { throw new JsonSerializationException("The object to serialize is null"); } Class clazz = object.getClass(); if (!clazz.isAnnotationPresent(JsonSerializable.class)) { throw new JsonSerializationException("The class " + clazz.getSimpleName() + " is not annotated with JsonSerializable"); } }

Dann suchen wir nach einer Methode mit @ Annit-Annotation und führen sie aus, um die Felder unseres Objekts zu initialisieren:

private void initializeObject(Object object) throws Exception { Class clazz = object.getClass(); for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(Init.class)) { method.setAccessible(true); method.invoke(object); } } }

Der Aufruf der Methode . Mit setAccessible ( true) können wir die private initNames () -Methode ausführen .

Nach der Initialisierung durchlaufen wir die Felder unseres Objekts, rufen den Schlüssel und den Wert von JSON-Elementen ab und fügen sie in eine Karte ein. Dann erstellen wir die JSON-Zeichenfolge aus der Map:

private String getJsonString(Object object) throws Exception { Class clazz = object.getClass(); Map jsonElementsMap = new HashMap(); for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); if (field.isAnnotationPresent(JsonElement.class)) { jsonElementsMap.put(getKey(field), (String) field.get(object)); } } String jsonString = jsonElementsMap.entrySet() .stream() .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"") .collect(Collectors.joining(",")); return "{" + jsonString + "}"; }

Wieder haben wir Feld verwendet . setAccessible ( tru e ) , weil die Person Felder sind privat Objekt.

Unsere JSON-Serializer-Klasse kombiniert alle oben genannten Schritte:

public class ObjectToJsonConverter { public String convertToJson(Object object) throws JsonSerializationException { try { checkIfSerializable(object); initializeObject(object); return getJsonString(object); } catch (Exception e) { throw new JsonSerializationException(e.getMessage()); } } }

Schließlich führen wir einen Komponententest durch, um zu überprüfen, ob unser Objekt gemäß unseren benutzerdefinierten Anmerkungen serialisiert wurde:

@Test public void givenObjectSerializedThenTrueReturned() throws JsonSerializationException { Person person = new Person("soufiane", "cheouati", "34"); JsonSerializer serializer = new JsonSerializer(); String jsonString = serializer.serialize(person); assertEquals( "{\"personAge\":\"34\",\"firstName\":\"Soufiane\",\"lastName\":\"Cheouati\"}", jsonString); }

4. Fazit

In diesem Artikel haben wir gesehen, wie verschiedene Arten von benutzerdefinierten Anmerkungen erstellt werden. Dann diskutierten wir, wie wir sie verwenden können, um unsere Objekte zu dekorieren. Schließlich haben wir uns angesehen, wie sie mit der Reflection-API von Java verarbeitet werden.

Wie immer ist der vollständige Code auf GitHub verfügbar.