Een aangepaste annotatie maken in Java

1. Inleiding

Java-annotaties zijn een mechanisme om metadata-informatie aan onze broncode toe te voegen. Ze zijn een krachtig onderdeel van Java en zijn toegevoegd in JDK5. Annotaties bieden een alternatief voor het gebruik van XML-descriptoren en markeringsinterfaces.

Hoewel we ze kunnen koppelen aan pakketten, klassen, interfaces, methoden en velden, hebben annotaties op zich geen effect op de uitvoering van een programma.

In deze zelfstudie gaan we ons concentreren op het maken van aangepaste annotaties en het verwerken ervan. We kunnen meer lezen over annotaties in ons artikel over basisprincipes van annotaties.

2. Aangepaste annotaties maken

We gaan drie aangepaste annotaties maken met als doel een object te serialiseren in een JSON-string.

We gebruiken de eerste op klassenniveau om aan de compiler aan te geven dat ons object kan worden geserialiseerd. Vervolgens passen we de tweede toe op de velden die we in de JSON-string willen opnemen.

Ten slotte gebruiken we de derde annotatie op methodeniveau om de methode te specificeren die we zullen gebruiken om ons object te initialiseren.

2.1. Voorbeeld annotatie op klasniveau

De eerste stap bij het maken van een aangepaste annotatie is om het aan te geven met behulp van de @koppel trefwoord:

openbaar @interface JsonSerializable {}

De volgende stap is om voeg meta-annotaties toe om het bereik en het doel te specificeren van onze aangepaste annotatie:

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

Zoals we kunnen zien, onze eerste annotatie heeft runtime-zichtbaarheid en we kunnen het toepassen op typen (klassen). Bovendien heeft het geen methoden en dient het dus als een eenvoudige markering om klassen te markeren die in JSON kunnen worden geserialiseerd.

2.2. Voorbeeld van aantekening op veldniveau

Op dezelfde manier maken we onze tweede annotatie om de velden te markeren die we gaan opnemen in de gegenereerde JSON:

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

De annotatie declareert één String-parameter met de naam “key” en een lege string als de standaardwaarde.

Bij het maken van aangepaste annotaties met methoden, moeten we ons ervan bewust zijn dat deze methoden mogen geen parameters hebben en kunnen geen uitzondering genereren. Ook, de retourtypen zijn beperkt tot primitieven, String, Class, enums, annotaties en arrays van deze typen,en de standaardwaarde mag niet null zijn.

2.3. Annotatievoorbeeld op het niveau van de methode

Laten we ons voorstellen dat, voordat we een object serialiseren naar een JSON-string, we een methode willen uitvoeren om een ​​object te initialiseren. Om die reden gaan we een annotatie maken om deze methode te markeren:

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

We hebben een openbare annotatie met runtime-zichtbaarheid gedeclareerd die we kunnen toepassen op de methoden van onze klassen.

2.4. Annotaties toepassen

Laten we nu eens kijken hoe we onze aangepaste annotaties kunnen gebruiken. Laten we ons bijvoorbeeld voorstellen dat we een object van type hebben Persoon die we willen serialiseren in een JSON-string. Dit type heeft een methode waarbij de eerste letter van de voor- en achternaam met een hoofdletter wordt geschreven. We willen deze methode aanroepen voordat we het object serialiseren:

@JsonSerializable public class Person {@JsonElement private String firstName; @JsonElement private String achternaam; @JsonElement (key = "personAge") private String leeftijd; privé String-adres; @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); } // Standaard getters en setters}

Door onze aangepaste annotaties te gebruiken, geven we aan dat we een Persoon object tegen een JSON-tekenreeks. Bovendien mag de uitvoer alleen de Voornaam, achternaam, en leeftijd velden van dat object. Bovendien willen we de initNames () methode die moet worden aangeroepen vóór serialisering.

Door de sleutel parameter van de @JsonElement annotatie naar "personAge", geven we aan dat we deze naam zullen gebruiken als de identificatie voor het veld in de JSON-uitvoer.

Ter wille van de demonstratie hebben we gemaakt initNames () private, dus we kunnen ons object niet initialiseren door het handmatig aan te roepen, en onze constructeurs gebruiken het ook niet.

3. Annotaties verwerken

Tot nu toe hebben we gezien hoe u aangepaste annotaties kunt maken en hoe u deze kunt gebruiken om het Persoon klasse. Nu, we gaan zien hoe we hiervan kunnen profiteren door Java's Reflection API te gebruiken.

De eerste stap zal zijn om te controleren of ons object dat is nul of niet, en ook of het type de @JsonSerializable annotatie of niet:

private void checkIfSerializable (Object-object) {if (Objects.isNull (object)) {throw new JsonSerializationException ("Het te serialiseren object is null"); } Klasse clazz = object.getClass (); if (! clazz.isAnnotationPresent (JsonSerializable.class)) {throw new JsonSerializationException ("The class" + clazz.getSimpleName () + "is niet geannoteerd met JsonSerializable"); }}

Vervolgens zoeken we naar een methode met @Init-annotatie en voeren we deze uit om de velden van ons object te initialiseren:

private void initializeObject (Object-object) gooit Uitzondering {Class clazz = object.getClass (); voor (Methode methode: clazz.getDeclaredMethods ()) {if (method.isAnnotationPresent (Init.class)) {method.setAccessible (true); method.invoke (object); }}}

De roep van methode.setAccessible(waar) stelt ons in staat om het privé uit te voeren initNames () methode.

Na de initialisatie herhalen we de velden van ons object, halen de sleutel en waarde van JSON-elementen op en plaatsen ze in een kaart. Vervolgens maken we de JSON-string van de kaart:

private String getJsonString (Object-object) genereert Uitzondering {Class clazz = object.getClass (); Kaart jsonElementsMap = nieuwe HashMap (); voor (Veldveld: clazz.getDeclaredFields ()) {field.setAccessible (true); if (field.isAnnotationPresent (JsonElement.class)) {jsonElementsMap.put (getKey (veld), (String) field.get (object)); }} String jsonString = jsonElementsMap.entrySet () .stream () .map (entry -> "\" "+ entry.getKey () +" \ ": \" "+ entry.getValue () +" \ "") .collect (Collectors.joining (",")); retourneer "{" + jsonString + "}"; }

Nogmaals, we gebruikten veld-.setAccessible(true) omdat de Persoon de velden van het object zijn privé.

Onze JSON-serialisatieklasse combineert alle bovenstaande stappen:

openbare klasse ObjectToJsonConverter {openbare String convertToJson (Object-object) gooit JsonSerializationException {probeer {checkIfSerializable (object); initializeObject (object); retourneer getJsonString (object); } catch (uitzondering e) {gooi nieuwe JsonSerializationException (e.getMessage ()); }}}

Ten slotte voeren we een eenheidstest uit om te valideren dat ons object is geserialiseerd zoals gedefinieerd door onze aangepaste annotaties:

@Test openbare ongeldig gegevenObjectSerializedThenTrueReturned () gooit JsonSerializationException {Person person = new Person ("soufiane", "cheouati", "34"); JsonSerializer serializer = nieuwe JsonSerializer (); String jsonString = serializer.serialize (persoon); assertEquals ("{\" personAge \ ": \" 34 \ ", \" firstName \ ": \" Soufiane \ ", \" lastName \ ": \" Cheouati \ "}", jsonString); }

4. Conclusie

In dit artikel hebben we gezien hoe u verschillende soorten aangepaste annotaties kunt maken. Daarna bespraken we hoe we ze konden gebruiken om onze objecten te versieren. Ten slotte hebben we gekeken hoe we ze konden verwerken met Java's Reflection API.

Zoals altijd is de volledige code beschikbaar op GitHub.