본문 바로가기

IT

Annotation(어노테이션) 만들기 + spring

728x90
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
    String value() default "";
}

스프링에서 제공하는 Component를 보자. 스프링은 @Component를 등록하여 bean을 만드는 방법이 있다. 

스프링은 ComponentScan으로 Component가 달려있는 Service, Repository 등을 스캔한다. 나는 이게 궁금했다.

 

이 Component에 대한 의구심이 들었고 그 전에 어노테이션은 어떻게 만드는지 부터 확인할 필요가 있었다.

  • @Documented : Java doc에 문서화 여부를 결정
  • @Retention : 어노테이션의 지속 시간을 결정.
    • RetentionPolicy.SOURCE : 컴파일 후에 정보들이 사라진다. 이 어노테이션은 컴파일이 완료된 후에는 의미가 없으므로, 바이트 코드에 기록되지 않는다. 예시) @Override와 @SuppressWarnings
    • RetentionPolicy.CLASS : default 값 입니다. 컴파일 타임 때만 .class 파일에 존재하며, 런타임 때는 없어진다. 바이트 코드 레벨에서 어떤 작업을 해야할 때 유용하다. Reflection 사용이 불가하다.
    • RetentionPlicy.RUNTIME : 이 어노테이션은 런타임시에도 .class 파일에 존재 합니다. 커스텀 어노테이션을 만들 때 주로 사용합니다. Reflection 사용이 가능하다.
  • @Target : 어노테이션을 사용하는 타겟이다. default 값은 모든 대상을 뜻한다. 예를 들어 @Target(ElementType.FIELD)로 지정해주면, 필드에만 어노테이션을 사용할 수 있다. 만약 필드 말고 다른부분에 어노테이션을 사용한다면 컴파일 때 에러가 날 것이다.
  • @Inherited : 자식클래스에 상속할지 결정한다.

 

커스텀 어노테이션을 만들 때 몇가지 규칙이 있다.

  1. 어노테이션 타입은 @interface로 정의 해야한다. 모든 어노테이션은 자동적으로 java.lang.Annotation 인터페이스를 상속하기 때문에 다른 클래스나 인터페이스를 상속 받으면 안된다.
  2. 파라미터 멤버들의 접근자는 public이거나 default여야만 한다.
  3. 파라미터 멤버들은 byte,short,char,int,float,double,boolean,의 기본타입과 String, Enum, Class, 어노테이션만 사용할 수 있다.
  4. 클래스 메소드와 필드에 관한 어노테이션 정보를 얻고 싶으면, 리플렉션만 이용해서 얻을 수 있다. 다른 방법으로는 어노테이션 객체를 얻을 수 없다.

Custom Annotation Example

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    enum Color{BLUE,RED,GREEN}

    Color fruitColor() default Color.GREEN;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {

    int id() default -1;

    String name() default "";

    String address() default "";
}

 

 

이제 이 어노테이션을 어떻게 사용하는지 확인해보자.

public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor = FruitColor.Color.RED)
    private String appleColor;

    @FruitProvider(id = 1,name = "HomePlus",address = "Seoul")
    private String appleProvider;
    
    ...getter setter
    }

간단했다. 우리가 spring에서 늘 사용하던 어노테이션 형식을 이런식으로 만들어서 사용하면 된다.

 

그러면 Component를 어떻게 스캔해서 빈을 등록하는지 확인해보자.

doScan() 메소드가 ClassPath에 있는 패키지의 모든 클래스를 읽어, Component 어노테이션이 붙은 클래스를 빈으로 등록해주는 클래스이다. 

Component 어노테이션이 사용된 클래스들을 모으게 된다.

어노테이션이 어디서 사용되었는지 알아야 하기 때문에, findCandidateComponents를 사용하여, Component 어노테이션이 사용된 클래스들을 전부 가져와 빈으로 등록하는 과정을 포함한다.