Crearea unei adnotări personalizate în Java

1. Introducere

Adnotările Java sunt un mecanism pentru adăugarea informațiilor despre metadate în codul nostru sursă. Acestea sunt o parte puternică a Java și au fost adăugate în JDK5. Adnotările oferă o alternativă la utilizarea descriptorilor XML și a interfețelor marker.

Deși le putem atașa la pachete, clase, interfețe, metode și câmpuri, adnotările de la sine nu au niciun efect asupra executării unui program.

În acest tutorial, ne vom concentra asupra modului de creare a adnotărilor personalizate și a modului de procesare a acestora. Putem citi mai multe despre adnotări în articolul nostru de bază despre adnotări.

2. Crearea de adnotări personalizate

Vom crea trei adnotări personalizate cu scopul de a serializa un obiect într-un șir JSON.

Vom folosi primul la nivel de clasă, pentru a indica compilatorului că obiectul nostru poate fi serializat. Apoi, îl vom aplica pe cel de-al doilea câmpurilor pe care dorim să le includem în șirul JSON.

În cele din urmă, vom folosi a treia adnotare la nivel de metodă, pentru a specifica metoda pe care o vom folosi pentru a inițializa obiectul nostru.

2.1. Exemplu de adnotare la nivel de clasă

Primul pas către crearea unei adnotări personalizate este să o declarați utilizând cuvântul cheie @interface :

public @interface JsonSerializable { }

Următorul pas este să adăugăm meta-adnotări pentru a specifica scopul și ținta adnotării noastre personalizate:

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

După cum putem vedea, prima noastră adnotare are vizibilitate în timpul rulării și o putem aplica tipurilor (claselor) . Mai mult, nu are metode și, astfel, servește ca un simplu marker pentru a marca clasele care pot fi serializate în JSON.

2.2. Exemplu de adnotare la nivel de câmp

În același mod, creăm a doua adnotare, pentru a marca câmpurile pe care urmează să le includem în JSON generat:

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

Adnotarea declară un parametru șir cu numele „cheie” și un șir gol ca valoare implicită.

Când creați adnotări personalizate cu metode, ar trebui să fim conștienți de faptul că aceste metode nu trebuie să aibă parametri și nu pot face excepție . De asemenea, tipurile de returnare sunt limitate la primitive, String, Class, enumerări, adnotări și tablouri ale acestor tipuri, iar valoarea implicită nu poate fi nulă .

2.3. Exemplu de adnotare la nivel de metodă

Să ne imaginăm că, înainte de serializarea unui obiect într-un șir JSON, vrem să executăm o metodă de inițializare a unui obiect. Din acest motiv, vom crea o adnotare pentru a marca această metodă:

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

Am declarat o adnotare publică cu vizibilitate în timpul rulării pe care o putem aplica metodelor claselor noastre.

2.4. Aplicarea adnotărilor

Acum, să vedem cum putem folosi adnotările noastre personalizate. De exemplu, să ne imaginăm că avem un obiect de tip Persoană pe care dorim să îl serializăm într-un șir JSON. Acest tip are o metodă care scrie cu majusculă prima literă a prenumelui și prenumelui. Vom dori să apelăm această metodă înainte de a serializa obiectul:

@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 }

Utilizând adnotările noastre personalizate, indicăm că putem serializa un obiect Person într-un șir JSON. În plus, ieșirea trebuie să conțină doar câmpurile prenume , prenume și vârstă ale acelui obiect. Mai mult, dorim ca metoda initNames () să fie apelată înainte de serializare.

Prin setarea parametrului cheie al adnotării @JsonElement la „personAge”, indicăm că vom folosi acest nume ca identificator pentru câmpul din ieșirea JSON.

Din motive de demonstrație, am făcut private initNames () , deci nu ne putem inițializa obiectul apelându -l manual și nici constructorii noștri nu îl folosesc.

3. Prelucrarea adnotărilor

Până acum, am văzut cum să creăm adnotări personalizate și cum să le folosim pentru a decora clasa Person . Acum, vom vedea cum să profităm de ele folosind API-ul Java Reflection.

Primul pas va fi să verificăm dacă obiectul nostru este nul sau nu, precum și dacă tipul său are sau nu adnotarea @JsonSerializable :

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

Apoi, căutăm orice metodă cu adnotare @Init și o executăm pentru a inițializa câmpurile obiectului nostru:

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

Chemarea metodei . setAccessible ( true) ne permite să execute privat initNames) ( metoda .

După inițializare, repetăm ​​câmpurile obiectului nostru, recuperăm cheia și valoarea elementelor JSON și le punem într-o hartă. Apoi, creăm șirul JSON din hartă:

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 + "}"; }

Din nou, am folosit câmpul . setAccessible ( Tru e ) , deoarece persoana câmpurile obiectului sunt private.

Clasa noastră de serializare JSON combină toți pașii de mai sus:

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

În cele din urmă, executăm un test de unitate pentru a valida faptul că obiectul nostru a fost serializat așa cum este definit de adnotările noastre personalizate:

@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. Concluzie

În acest articol, am văzut cum să creăm diferite tipuri de adnotări personalizate. Apoi am discutat despre cum să le folosim pentru a ne decora obiectele. În cele din urmă, ne-am uitat la modul de procesare a acestora folosind Java's Reflection API.

Ca întotdeauna, codul complet este disponibil pe GitHub.