Рубрики
Без рубрики

Создание пользовательской аннотации в Java

Узнайте, как создать и обработать пользовательскую аннотацию в Java

Автор оригинала: baeldung.

1. введение

Аннотации Java-это механизм добавления метаданных в наш исходный код. Они являются мощной частью Java и были добавлены в JDK 5. Аннотации предлагают альтернативу использованию XML-дескрипторов и интерфейсов маркеров.

Хотя мы можем прикреплять их к пакетам, классам, интерфейсам, методам и полям, аннотации сами по себе не влияют на выполнение программы.

В этом уроке мы сосредоточимся на том, как создавать пользовательские аннотации и как их обрабатывать. Подробнее об аннотациях мы можем прочитать в нашей статье об основах аннотаций .

Дальнейшее чтение:

Абстрактные классы в Java

Интерфейсы маркеров в Java

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

В аннотации объявляется один строковый параметр с именем “ключ” и пустой строкой в качестве значения по умолчанию.

При создании пользовательских аннотаций с помощью методов мы должны знать, что эти методы не должны иметь параметров и не могут вызывать исключение . Кроме того, возвращаемые типы ограничены примитивами, строками, классами, перечислениями, аннотациями и массивами этих типов, и значение по умолчанию не может быть null .

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() вызывался перед сериализацией.

Установив параметр key аннотации @JsonElement в “personAge”, мы указываем, что будем использовать это имя в качестве идентификатора поля в выводе JSON.

Для демонстрации мы сделали initNames() private, поэтому мы не можем инициализировать наш объект, вызывая его вручную, и наши конструкторы также не используют его.

3. Обработка аннотаций

До сих пор мы видели, как создавать пользовательские аннотации и как использовать их для украшения класса Person . Теперь, мы увидим, как воспользоваться ими, используя API отражения Java.

Первым шагом будет проверка того, является ли наш объект null или нет, а также имеет ли его тип аннотацию @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 ( true) позволяет нам выполнить частный 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 ( true 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 .