ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 12주차 과제: 애노테이션
    Programming/Java live study 2021. 2. 3. 16:21
     

    12주차 과제: 애노테이션 · Issue #12 · whiteship/live-study

    목표 자바의 애노테이션에 대해 학습하세요. 학습할 것 (필수) 애노테이션 정의하는 방법 @retention @target @documented 애노테이션 프로세서 마감일시 2021년 2월 6일 토요일 오후 1시까지.

    github.com

    목표

     자바의 애노테이션에 대해 학습하세요.

    학습할 것

     - 애노테이션 정의하는 방법

     - @Retention

     - @Target

     - @Documented

     - 애노테이션 프로세서


    1. 애노테이션이란?

    참고 :

    honbabzone.com/java/java-anontation/

    b-programmer.tistory.com/264

    gowoonsori.site/java/annotation/

    1.1 애노테이션

    애노테이션은 컴파일 과정에서 코드를 어떻게 컴파일할 것인지, 실행 과정에서 코드를 어떻게 처리할 것인지를 알려주는 정보이다. 즉 메타 데이터로써의 역할을 한다. 메타 데이터는 데이터를 위한 설명을 의미하는 데이터가 담겨있다.

     

     주로 컴파일러에게 코드 문법 에러를 검사하도록 정보를 제공하거나 코드를 자동으로 생성할 수 있도록 정보를 제공한다. 런타임 시에는 특정 기능을 실행하도록 정보를 제공한다.

     

     사용 방식은 @ + 애노테이션 명으로 특정 클래스나 메소드, 변수에 붙여 사용한다.

     

     애노테이션은 주석과 비슷한 류의 장치이다. 해당 장치를 한다는 표시를 남겨둔 것이다. 해당 애노테이션이 어떠한 기능을 가진 것은 아니다. 또한 럼타임 중에 알아내야 하는 값, 즉 동적인 값은 들어갈 수 없다. 정적인 값, 컴파일러 수준에서 해석이 가능한 값들로만 엘리먼트로 들어갈 수 있다.

     

    일반적인 String 변수는 들어갈 수 없다. 상수가 요구된다.

    1.3 빌트인 애노테이션

     Java에 내장되어 있는 애노테이션으로 컴파일러를 위한 애노테이션이다.

     

     - @Override

     - @Deprecated

     - @SupperssWarning

     - @SafeVarargs

     - @FunctionalInterface

    1.4 메타 애노테이션

     애노테이션에 사용되는 애노테이션으로 애노테이션을 정의하고 설명하기 위해 사용된다. 

     

     - @Retention

     - @Target

     - @Documented

     - @Inherited

     - @Repeatable

    1.5 빌트인 애노테이션의 사용

     - @Override

      - 선언한 메소드가 오버라이드 되었다는 것을 나타낸다.

      - 상위 클래스에서 해당 메소드를 찾을 수 없으면 컴파일 에러를 발생 시킨다.

     

     - @Deprecated

      - 해당 메소드가 더 이상 사용되지 않음을 표시한다.

      - 사용할 경우 컴파일 경고를 발생 시킨다.

      - 해당 메소드를 무작정 삭제하게 되면 기존에 메소드를 사용하던 개발자들은 직접 수정 작업을 거쳐야 한다. 하지만 @Deprecated 메소드만 붙여주면 이전의 소스코드 또한 호환이 가능하고, 앞으로 작성될 코드들에게는 메소드를 사용하지 말라는 경고를 남겨 사용하지 않게 방지할 수 있다. 결국 호환성을 위하여 존재하는 애노테이션이다.

     

     - @SuppressWarnings

      - 선언한 곳의 컴파일 경고를 무시한다.

     

     - @SafeVarargs

      - Java 7 부터 지원하고, 제네릭 같은 가변인자의 매개변수를 사용할 때 경고를 무시한다.

     

     - @FunctionalInterface

      - Java 8 부터 지원한다. 함수형 인터페이스를 지정하는 애노테이션이다.

      - 메소드가 존재하지 않거나, 1개 이상의 메소드가 존재할 경우 컴파일 오류를 발생 시킨다.

     

     간단한 예제를 작성하여 위의 애노테이션을 사용해보았다.

     

    public class Item {
    
        private String name;
        private int price;
    
        public Item(String name, int price) {
            this.name = name;
            this.price = price;
        }
    
        public String printInfo() {
            String info = "이름: " + name + "\n"
                        + "가격: " + price + "\n";
    
            return info;
        }
    
        @Deprecated
        public void changePrice(int price) {
            this.price = price;
        }
    }

     

     가격은 최초에 등록한 가격 이외에 변동을 제한한다고 가정했다. 해당 메소드를 사용하지 않게 사용자에게 알려주기 위해 @Deprecated를 붙였다.

     

    deprecated

     해당 메소드를 사용하지 말라고 경고하는 것을 확인할 수 있다. 경고만 할 뿐, 무시하고 사용은 가능하다.

     

    public class Book extends Item {
    
        private String author;
        private String publisher;
    
        public Book(String name, int price, String author, String publisher) {
            super(name, price);
            this.author = author;
            this.publisher = publisher;
        }
    
        @Override
        public String printInfo() {
            String info = super.printInfo();
            info += "저자 : " + author + "\n"
                  + "출판사: " + publisher + "\n";
    
            return info;
        }
    }

     

     printInfo는 아이템의 정보를 반환하는 메소드이다. Book 클래스에 추가로 들어온 필드가 있기 때문에 메소드를 재정의하였고, 상위 클래스에 prinfInfo 메소드가 있는지 @Override를 통하여 확인할 수 있었다.

     

    public class Main {
    
        public static void main(String[] args) {
    
            Item item1 = new Item("커피", 3_800);
            Book book1 = new Book("오브젝트", 38_000, "조영호", "위키북스");
            Book book2 = new Book("객체지향의 사실과 오해", 20_000, "조영호", "위키북스");
    
            item1.changePrice(1000); // 경고를 무시하고 강제로 사용
    
            System.out.println(item1.printInfo()); // Item 클래스의 printInfo 사용
     
            System.out.println(book1.printInfo()); // 오버라이딩된 printInfo 사용 
            System.out.println(book2.printInfo()); // 오버라이딩된 printInfo 사용 
        }
    }

     

    이름: 커피
    가격: 1000
    
    이름: 오브젝트
    가격: 38000
    저자 : 조영호
    출판사: 위키북스
    
    이름: 객체지향의 사실과 오해
    가격: 20000
    저자 : 조영호
    출판사: 위키북스

     

    SuppressWarnings

     @SuppressWarnings 애노테이션에 "deprecation" 속성을 넣으면 사용하지 말아야 하는 메소드 관련 경고를 억제하기 때문에 changePrice의 컴파일 경고가 무시된다! 그 밖에도 다양한 속성은 이곳에서 확인할 수 있다.

    www.ibm.com/support/knowledgecenter/ko/SS8PJ7_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.html

     

     @FunctionalInterface는 Runnable Interface에서 확인할 수 있었다.

     

    FunctioalInterface

     해당 인터페이스가 함수형 인터페이스임을 암시한다. 해당 인터페이스에 메소드가 존재하지 않거나 하나 이상 존재하면 컴파일 에러를 발생시킨다.

     

     확인해보기 위해 함수형 인터페이스를 임의로 생성하였다.

     

    컴파일 에러 발생

     두개 이상 메소드를 선언했다. 컴파일 에러가 발생한 것을 확인할 수 있었다.


    2. 애노테이션 정의하는 방법

    참고 : honbabzone.com/java/java-anontation/

     

     애노테이션 타입을 정의하기 위해서는 @interface를 사용하여 정의한다.

     

    public @interface CustomAnnotation {
    
    }

     

     애노테이션은 Element라는 것을 멤버로 가질 수 있다. Element는 타입과 이름으로 구성되며 default 값을 가질 수 있다. Element의 이름 뒤에는 메소드를 작성하는 것 처럼 뒤에 ()를 붙여야 한다.

     

    public @interface CustomAnnotation {
    
        // Element
        String value();
        String description();
    }

     

     이렇게 정의한 애노테이션은 아래 처럼 사용할 수 있다.

     

    @CustomAnnotation(value = "25", description = "올해의 나이")

     

     기본 Element는 value이며, 해당하는 값은 생략하고 바로 사용이 가능하다.

     

    @CustomAnnotation("25")

    3. @Retention

    참고 : honbabzone.com/java/java-anontation/

     

     애노테이션 선언에 사용되는 메타 애노테이션이다. Java 컴파일러가 애노테이션을 다루는 방법을 기술한다. 어느 시점까지 영향을 미치는지 결정한다. 해당 정책들은 java.lang.annotation.RetentionPolicy에 enum으로 정의되어 있다.

     

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }

     

     - RetentionPolicy.SOURCE: 컴파일 전까지 유효하다. 컴파일 이후에는 사라진다.

     

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.SOURCE)
    public @interface SourceAnnotation {
    
        String value();
    }

     

     RetentionPolicy를 SOURCE로 설정하였다.

     

    import me.hyeonic.week12.annotation.SourceAnnotation;
    
    @SourceAnnotation("source")
    public class Exam02 {
    }

     

     밑은 컴파일된 바이트코드이다. @SourceAnnotation의 존재를 확인 할 수 없다.

     

    // class version 55.0 (55)
    // access flags 0x21
    public class me/hyeonic/week12/Exam02 {
    
      // compiled from: Exam02.java
    
      // access flags 0x1
      public <init>()V
       L0
        LINENUMBER 6 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.<init> ()V
        RETURN
       L1
        LOCALVARIABLE this Lme/hyeonic/week12/Exam02; L0 L1 0
        MAXSTACK = 1
        MAXLOCALS = 1
    }

     

     - RetentionPolicy.CLASS: 컴파일러가 클래스를 참조할 때 까지 유효하다. 바이트코드에서 해당 애노테이션의 존재를 확인할 수 있다.

     

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.CLASS)
    public @interface ClassAnnotation {
    
        String value();
    }

     

    import me.hyeonic.week12.annotation.ClassAnnotation;
    
    @ClassAnnotation("class")
    public class Exam03 {
    }

     

    // class version 55.0 (55)
    // access flags 0x21
    public class me/hyeonic/week12/Exam03 {
    
      // compiled from: Exam03.java
    
      @Lme/hyeonic/week12/annotation/ClassAnnotation;(value="class") // invisible
    
      // access flags 0x1
      public <init>()V
       L0
        LINENUMBER 6 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.<init> ()V
        RETURN
       L1
        LOCALVARIABLE this Lme/hyeonic/week12/Exam03; L0 L1 0
        MAXSTACK = 1
        MAXLOCALS = 1
    }

     

     SOURCE와 동일한 예시이지만 바이트코드에서 해당 애노테이션의 존재를 확인 할 수 있다.

     

     - RetentionPolicy.RUNTIME:  클래스 로더가 클래스를 메모리에 적재하는 시점에 애노테이션이 등록된다. 그렇기 때문에 리플렉션을 이용하여 런타임시에도 애노테이션 정보를 얻을 수 있다. 리플렉션은 Runtime 시에도 클래스의 메타 정보를 얻는 기능을 말한다. 

     

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RuntimeAnnotation {
    
        String value();
    }

     

    import me.hyeonic.week12.annotation.RuntimeAnnotation;
    
    @RuntimeAnnotation("runtime")
    public class Exam04 {
    }

     

    // class version 55.0 (55)
    // access flags 0x21
    public class me/hyeonic/week12/Exam04 {
    
      // compiled from: Exam04.java
    
      @Lme/hyeonic/week12/annotation/RuntimeAnnotation;(value="runtime")
    
      // access flags 0x1
      public <init>()V
       L0
        LINENUMBER 6 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.<init> ()V
        RETURN
       L1
        LOCALVARIABLE this Lme/hyeonic/week12/Exam04; L0 L1 0
        MAXSTACK = 1
        MAXLOCALS = 1
    }

     

     Runtime에는 클래스가 애노테이션 정보를 유지해야 하기 때문에 CLASS와 다르게 // invisible 주석이 없는 것을 알 수 있다.

     

     커스텀한 애노테이션을 사용할 때 고려해야 할 점은 해당 애노테이션을 런타임에도 사용할지에 대한 고민이 필요하고, 굳이 사용하지 않고 주석의 용도로써 사용한다면 SOURCE로 정책을 변경하는 것이 바람직하다.

     

    바이트코드를 활용하여 애노테이션 정보를 읽는 것의 속도 vs 리플렉션으로 읽어오는 속도

     - 바이트코드를 읽어서 정보를 가져오는 것이 빠를지, 클래스 로더가 읽은 클래스를 읽는 것이 빠를지 직접 시간을 측정하여 비교해보는 것이 좋다. 백기선님 예상으로는 바이트코드를 읽는 것이 빠를 것으로 예상.


    4. @Target

     애노테이션 선언에 사용되는 메타 애노테이션이다. 애노테이션을 적용할 수 있는 범위를 정의한다. java.lang.annotation.ElementType에 다양한 범위가 정의되어 있다.

     

     - ElementType.TYPE: 클래스, 인터페이스, 열거 타입에 적용한다.

     - ElementType.ANNOTATION_TYPE: 애노테이션에 적용한다.

     - ElementType.FIELD: 필드에 적용한다.

     - ElementType.CONSTRUCTOR: 생성자에 적용한다.

     - ElementType.METHOD: 메소드에 적용한다.

     - ElementType.LOCAL_VARIABLE: 로컬 변수에 사용한다.

     - ElementType.PACKAGE: 패키지에 사용한다.

     

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }

     

     애노테이션이 적용될 대상을 지정할 때 사용한다. @Target의 기본 엘리먼트는 ElementType 배열의 값을 가질 수 있다. 또한 필드명이 value이기 때문에 필드명 생략이 가능하다.

     

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.FIELD})
    public @interface CustomAnnotation {
    
        // Element
        String description();
    }

     

     위 애노테이션은 메소드와 필드에 사용할 수 있게 대상을 한정한다.

     

    public class Item {
    
        @CustomAnnotation(description = "item 의 이름을 저장하는 name 변수")
        private String name;
        @CustomAnnotation(description = "item 의 가격을 저장하는 price 변수")
        private int price;
    
        public Item(String name, int price) {
            this.name = name;
            this.price = price;
        }
    
        public String printInfo() {
            String info = "이름: " + name + "\n"
                        + "가격: " + price + "\n";
    
            return info;
        }
    
        @CustomAnnotation(description = "price 의 값을 변경하는 메소드")
        public void changePrice(int price) {
            this.price = price;
        }
    }

     

     변수와 메소드에 붙여서 사용가능하다.


    5. @Documented

    참고: b-programmer.tistory.com/264

     

     애노테이션 선언에 사용되는 메타 애노테이션이다. 해당 애노테이션에 대하 정보가 Javadoc문서에 포함된다는 것을 선언한다.

     

    tools의 generate JavaDoc를 클릭한다.

     Locale에는 지역을 입력한다.

     Other command line arguments는 한글깨짐 방지를 위하여 설정한다.

      -encoding UTF-8 -charset UTF-8 -docencoding UTF-8

     

     저장할 디렉토리 위치를 output directory에 경로를 설정한다.

     

     이제 @Documented가 붙은 것과 안 붙은 것을 비교해 보았다.

     

    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface JavaDocAnnotation {
    }

     

    @JavaDocAnnotation
    public class Exam05 {
    }

     

     @Documented가 붙은 JavaDocAnnotation이다.

     

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NoneJavaDocAnnotation {
    }

     

    import me.hyeonic.week12.annotation.NoneJavaDocAnnotation;
    
    @NoneJavaDocAnnotation
    public class Exam06 {
    }

     

     @Documented가 붙지 않은 NoneJavaDocAnnotation이다.

     

    애노테이션의 유무를 확인 할 수 있었다.

     

    이러한 JavaDoc를 적극 활용한 사이트의 예로는 Mockito가 있다.

    javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html


    6. @Inherited

     상속 받은 클래스에도 해당 애노테이션을 유지하기 위해 붙인다.

     

    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InheritedAnnotation {
    
        String value() default "";
    }

     

    import me.hyeonic.week12.annotation.InheritedAnnotation;
    
    @InheritedAnnotation
    public class Item {
    
        @CustomAnnotation(description = "item 의 이름을 저장하는 name 변수")
        private String name;
        @CustomAnnotation(description = "item 의 가격을 저장하는 price 변수")
        private int price;
    
        public Item() {
        }
    
        public Item(String name, int price) {
            this.name = name;
            this.price = price;
        }
    
        public String printInfo() {
            String info = "이름: " + name + "\n"
                        + "가격: " + price + "\n";
    
            return info;
        }
    
        @CustomAnnotation(description = "price 의 값을 변경하는 메소드")
        public void changePrice(int price) {
            this.price = price;
        }
    }

     

    public class Book extends Item {
    
        private String author;
        private String publisher;
    
        public Book() {
            super();
        }
    
        public Book(String name, int price, String author, String publisher) {
            super(name, price);
            this.author = author;
            this.publisher = publisher;
        }
    
        @Override
        public String printInfo() {
            String info = super.printInfo();
            info += "저자 : " + author + "\n"
                  + "출판사: " + publisher + "\n";
    
            return info;
        }
    }

     

    import java.lang.annotation.Annotation;
    
    public class Exam07 {
    
        public static void main(String[] args) {
    
            Book book = new Book();
            Annotation[] annotations = book.getClass().getAnnotations();
    
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
    }

     

    @me.hyeonic.week12.annotation.InheritedAnnotation(value="")

     

     리플렉션을 이용하여 book에서 사용하는 애노테이션과 @Inherited가 붙은 상위 클래스의 애노테이션을 출력하는 것을 확인 할 수 있었다.

     

    - XXX.class.getSuperClass().getAnnotations() -> 상위 클래스의 애노테이션을 볼 수 있다.

    - XXX.class.getDeclaredAnnotations() -> @Inherited 애노테이션을 무시하고 현재 클래스에 붙어 있는 애노테이션을 구한다.


    7. 애노테이션 프로세서

    참고 :

    better-dev.netlify.app/java/2020/09/07/thejava_16/

    www.notion.so/12-95595cad188b45058bfb1ddcf97869c5

     

     애노테이션을 이용하여 프로세스를 처리하는 것을 의미한다. 특히 컴파일 단계에서 애노테이션에 정의된 액션을 처리하는데, 이것은 애플리케이션이 실행되기 전에 체킹을 해주기 때문에 애노테이션이 의도한대로 이루어지지 않으면 눈으로 확인할 수 있는 에러나 경고를 보여준다.

     

     애노테이션 프로세서의 가장 대표적인 예로는 Lombok 라이브러리가 있다. 

     

     getter/setter와 같이 반복적이고 중복되는 많은 코드를 양산하는 코드 (boilerplate code) 를 최소화하고 핵심적인 기능을 명확하게 확인할 수 있게 도와준다.

     

     애노테이션 프로세서를 작성하고 등록하기 위해서는 AbstractProcessror 클래스를 상속받아야 한다. Lombok 또한 AbstractProcessor를 상속받아 애노테이션 프로세서를 정의하였다. 

     

     간단한 이해를 위해 lombok을 사용한 예제를 작성하였다.

     

    import lombok.Getter;
    import lombok.Setter;
    
    @Getter @Setter
    public class Member {
    
        private String name;
        private int age;
    }

     

     lombok의 @Getter @Setter 애노테이션을 사용하였다. 클래스 파일을 살펴보았다.

     

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package me.hyeonic.annotationprocessor;
    
    public class Member {
        private String name;
        private int age;
    
        public Member() {
        }
    
        public String getName() {
            return this.name;
        }
    
        public int getAge() {
            return this.age;
        }
    
        public void setName(final String name) {
            this.name = name;
        }
    
        public void setAge(final int age) {
            this.age = age;
        }
    }

     

     getter와 setter 메소드를 따로 작성하지 않았지만 자동적으로 추가된 것을 확인 할 수 있다.

     

     이러한 동작을 하기 위해서는 애노테이션 프로세서를 작성하고 등록해야 한다.

     

     lombok에도 이러한 애노테이션 프로세서를 작성하기위해 AbstractPrecessor를 상속받아 애노테이션 프로세서를 정의한다. 

     

    class AnnotationProcessorHider {
    
    	public static class AstModificationNotifierData {
    		...
    	}
    	
    	public static class AnnotationProcessor extends AbstractProcessor {
    		...
    	}
    	
    	@SupportedAnnotationTypes("lombok.*")
    	public static class ClaimingProcessor extends AbstractProcessor {
    		...
    	}
    }
    

     

     이러한 형식으로 정의되어 있는 것을 확인 할 수 있다.

     

     또한 애노테이션 프로세서를 사용하기 위해 등록하는 과정이 필요하다. 이때 Service Loader를 사용한 패턴을 활용하여 해당 구현체를 등록해줘야 한다.

     

     META-INF/services 디렉토리에 javax.annotation.processing.Processor 파일의 살펴보면 구현체의 목록을 확인할 수 있다.

     

    lombok.launch.AnnotationProcessorHider$AnnotationProcessor
    lombok.launch.AnnotationProcessorHider$ClaimingProcessor

    8. Java의 ServiceLoader

     위에서 애노테이션 프로세서를 구현하기 위해 Service loader를 사용한 패턴이 이용된 것이다.

     

     만약 특정한 인터페이스를 선언했다고 가정하자. 구현체는 여러 곳에서 제각각의 jar 파일로 생성이 가능하다. 예를들면 google.jar, naver.jar, kakao.jar 등등 다양한 구현체로 생성이 가능하다.

     

     인터페이스를 사용하는 개발자는 해당 인터페이스의 구현체를 모른다. 해당 구현체를 직접 지정하는 것이 아니라 jar 파일만 바꿔 끼면 구현체의 인스턴스를 사용할 수 있다. 그것을 가져오도록 지원하는 것이 java ServiceLoader이다. ServiceLoader는 인터페이스의 이름이다.

     

     간단한 예를 들기 위해 3개의 프로젝트를 생성하였다.

    8.1 hello-service 프로젝트

     hello-service 프로젝트 안에는 HelloService 인터페이스가 선언되어 있다. 해당 프로젝트를 maven을 활용하여 install 하여 jar 파일을 생성한다.

    8.2 my-hello 프로젝트

     hello-service를 구현하기 위한 my-hello 프로젝트를 생성하였다. 

     

     1. 프로젝트를 생성한 후 hello-service 구현을 위하여 pom.xml에 의존성의 추가한다.

     2. MyHello로 HelloService를 구현한다.

     3. resources 하위에 META-INF/services 디렉토리를 생성한다.

     4. 인터페이스가 들어있는 풀패키지 경로로 파일 이름을 설정하고 생성한다.

     5. 파일 내용으로는 해당 인터페이스를 구현한 FQCN (Fully Qualified Class Name) 을 기재한다.

     

     모든 작업을 마친 후 maven install 하여 jar 파일을 생성한다.

    8.3 hello-app

     앞서 구현한 my-hello를 pom.xml에 추가한 후, my-hello와 더불어 hello-service가 추가된 것을 확인할 수 있다. 간단하게 ServiceLoader를 사용하여 구현체의 메소드를 출력해보았다.

     

    my-hello의 hello 메소드

     정상적으로 실행된 것을 알 수 있다.

     

     lombok을 직접 제작한 선원분의 게시글을 참고해보면 더 자세한 내용을 확인할 수 있었다.

    catch-me-java.tistory.com/49


    References.

    honbabzone.com/java/java-anontation/

    www.ibm.com/support/knowledgecenter/ko/SS8PJ7_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.html

    better-dev.netlify.app/java/2020/09/07/thejava_16/

    https://b-programmer.tistory.com/264

    https://www.notion.so/12-95595cad188b45058bfb1ddcf97869c5

    catch-me-java.tistory.com/49

    javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

    b-programmer.tistory.com/264

    gowoonsori.site/java/annotation/

     

     

    'Programming > Java live study' 카테고리의 다른 글

    14주차 과제: 제네릭  (0) 2021.02.24
    13주차 과제: I/O  (0) 2021.02.14
    11주차 과제: Enum  (0) 2021.01.27
    10주차 과제: 멀티쓰레드 프로그래밍  (0) 2021.01.21
    9주차 과제: 예외 처리  (0) 2021.01.13

    댓글

Designed by Tistory.