Java에서 사용자 지정 주석 만들기

in #kr-dev2 years ago

1. 소개

Java 주석은 소스 코드에 메타데이터 정보를 추가하는 메커니즘입니다. JDK5에 추가된 Java의 강력한 부분입니다. 주석은 XML 디스크립터 및 마커 인터페이스 사용에 대한 대안을 제공합니다.

주석을 패키지, 클래스, 인터페이스, 메소드 및 필드에 첨부할 수 있지만 주석 자체는 프로그램 실행에 영향을 미치지 않습니다.

이 자습서에서는 사용자 지정 주석을 만들고 처리하는 방법에 중점을 둘 것입니다. 주석 기본 사항에 대한 문서에서 주석 에 대해 자세히 알아볼 수 있습니다 .

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

주석은 "key"라는 이름과 빈 문자열을 기본값으로 하는 하나의 문자열 매개변수를 선언합니다.

메소드로 사용자 지정 주석을 생성할 때 이러한 메소드에는 매개변수가 없어야 하며 예외를 발생시킬 수 없다는 점에 유의해야 합니다 . 또한 반환 유형은 프리미티브, 문자열, 클래스, 열거형, 주석 및 이러한 유형의 배열로 제한되며 기본값은 null 이 될 수 없습니다 .

2.3. 메서드 수준 주석 예

개체를 JSON 문자열로 직렬화하기 전에 개체를 초기화하기 위해 몇 가지 메서드를 실행하려고 한다고 상상해 봅시다. 이러한 이유로 이 메서드를 표시하는 주석을 만들 것입니다.

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

우리는 클래스의 메서드에 적용할 수 있는 런타임 가시성과 함께 공개 주석을 선언했습니다.

2.4. 주석 적용

이제 사용자 지정 주석을 사용하는 방법을 살펴보겠습니다. 예를 들어 JSON 문자열로 직렬화하려는 Person 유형의 개체가 있다고 가정해 보겠습니다. 이 타입은 성과 이름의 첫 글자를 대문자로 하는 방식이 있습니다. 개체를 직렬화하기 전에 이 메서드를 호출해야 합니다.

@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 , lastNameage 필드만 포함되어야 합니다. 또한 initNames() 메서드가 직렬화 전에 호출되기를 원합니다.

@JsonElement 주석 의 매개변수 를 "personAge"로 설정하면 JSON 출력에서 이 이름을 필드의 식별자로 사용함을 나타냅니다.

시연을 위해 initNames() 를 비공개로 만들었으므로 객체를 수동으로 호출하여 초기화할 수 없고 생성자도 객체를 사용하지 않습니다.

3. 처리 주석

지금까지 사용자 지정 주석을 만드는 방법과 이를 사용하여 Person 클래스 를 장식하는 방법을 살펴보았습니다 . 이제 Java의 Reflection API를 사용하여 이를 활용하는 방법을 살펴보겠습니다.

첫 번째 단계는 객체가 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<String, String> 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 + "}";
}

다시, 우리는 field 를 사용했습니다 . Person 개체의 필드가 비공개 이기 때문에 setAccessible ( tru e ) 입니다.

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");
    ObjectToJsonConverter serializer = new ObjectToJsonConverter(); 
    String jsonString = serializer.convertToJson(person);
    assertEquals(
      "{\"personAge\":\"34\",\"firstName\":\"Soufiane\",\"lastName\":\"Cheouati\"}",
      jsonString);
}

4. 결론

이 기사에서는 다양한 유형의 사용자 정의 주석을 작성하는 방법을 배웠습니다. 그런 다음 그것들을 사용하여 물건을 장식하는 방법에 대해 논의했습니다. 마지막으로 Java의 Reflection API를 사용하여 처리하는 방법을 살펴보았습니다.

항상 그렇듯이 전체 코드는 GitHub 에서 사용할 수 있습니다 .

출처 : https://www.baeldung.com/java-custom-annotation

Sort:  

[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.