1. Въведение
Java анотациите са механизъм за добавяне на информация за метаданни към нашия изходен код. Те са мощна част от Java и са добавени в JDK5. Анотациите предлагат алтернатива на използването на XML дескриптори и интерфейси на маркери.
Въпреки че можем да ги прикачим към пакети, класове, интерфейси, методи и полета, анотациите сами по себе си нямат ефект върху изпълнението на програма.
В този урок ще се съсредоточим върху това как да създаваме персонализирани пояснения и как да ги обработваме. Можем да прочетем повече за анотациите в нашата статия за основите на анотациите.
2. Създаване на персонализирани пояснения
Ще създадем три персонализирани анотации с цел сериализиране на обект в JSON низ.
Ще използваме първия на ниво клас, за да посочим на компилатора, че нашият обект може да бъде сериализиран. След това ще приложим второто към полетата, които искаме да включим в низа JSON.
И накрая, ще използваме третата анотация на ниво метод, за да определим метода, който ще използваме за инициализиране на нашия обект.
2.1. Пример за анотация на ниво клас
Първата стъпка към създаването на персонализирана анотация е да я декларирате с помощта на ключовата дума @interface :
public @interface JsonSerializable { }
Следващата стъпка е да добавите мета-анотации, за да посочите обхвата и целта на нашата персонализирана анотация:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.Type) public @interface JsonSerializable { }
Както виждаме, първата ни анотация има видимост по време на изпълнение и можем да я приложим към типове (класове) . Освен това той няма методи и по този начин служи като прост маркер за маркиране на класове, които могат да бъдат сериализирани в JSON.
2.2. Пример за анотация на ниво поле
По същия начин създаваме втората си анотация, за да маркираме полетата, които ще включим в генерирания JSON:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface JsonElement { public String key() default ""; }
Анотацията декларира един параметър String с името „ключ“ и празен низ като стойност по подразбиране.
Когато създаваме персонализирани пояснения с методи, трябва да сме наясно, че тези методи не трябва да имат параметри и не могат да създават изключение . Също така типовете връщане са ограничени до примитиви, String, Class, enum, анотации и масиви от тези типове и стойността по подразбиране не може да бъде нула .
2.3. Пример за анотация на ниво метод
Нека си представим, че преди да сериализираме обект в JSON низ, искаме да изпълним някакъв метод за инициализиране на обект. Поради тази причина ще създадем анотация, за да маркираме този метод:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Init { }
Декларирахме публична анотация с видимост по време на изпълнение, която можем да приложим към методите на нашите класове.
2.4. Прилагане на анотации
Сега нека видим как можем да използваме нашите персонализирани пояснения. Например, нека си представим, че имаме обект от тип Person , който искаме да сериализираме в JSON низ. Този тип има метод, който изписва с главна буква първата буква на собственото и фамилното име. Ще искаме да извикаме този метод, преди да сериализираме обекта:
@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 }
Използвайки нашите персонализирани анотации, ние посочваме, че можем да сериализираме обект Person към JSON низ. Освен това изходът трябва да съдържа само полетата firstName , lastName и age на този обект. Освен това искаме методът initNames () да бъде извикан преди сериализацията.
Чрез определянето на ключов параметър на @JsonElement анотацията към "личност", ние сме се посочва, че ще използваме това име, тъй като идентификатор за областта в продукцията JSON.
За демонстрация направихме initNames () частни, така че не можем да инициализираме обекта си, като го извикаме ръчно и нашите конструктори също не го използват.
3. Обработка на анотации
Досега видяхме как да създаваме персонализирани пояснения и как да ги използваме за украса на класа Person . Сега ще видим как да се възползваме от тях, като използваме API за отразяване на Java.
Първата стъпка ще бъде да проверим дали обектът ни е нулев или не, както и дали неговият тип има анотация @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"); } }
След това търсим всеки метод с @Init анотация и го изпълняваме, за да инициализираме полетата на нашия обект:
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); } } }
Призивът на метода . setAccessible ( вярно) ни позволява да изпълнява частни initNames () метод .
След инициализацията прелистваме полетата на нашия обект, извличаме ключа и стойността на JSON елементите и ги поставяме в карта. След това създаваме JSON низ от картата:
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 + "}"; }
Отново използвахме поле . setAccessible ( tru e ), защото полетата на обекта Person са частни.
Нашият клас JSON сериализатор съчетава всички горепосочени стъпки:
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()); } } }
Накрая изпълняваме единичен тест, за да потвърдим, че обектът ни е сериализиран, както е дефинирано от нашите персонализирани анотации:
@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. Заключение
В тази статия видяхме как да създаваме различни видове персонализирани пояснения. След това обсъдихме как да ги използваме за украса на нашите предмети. Накрая разгледахме как да ги обработим с помощта на API за отразяване на Java.
Както винаги, пълният код е достъпен в GitHub.