6 Nov 2020

java custom annotation

Custom Annotation

코드를 작성하다보면 아래 처럼 필드값에 Validation을 적용하고 싶은 경우가 있다.

@Validation(min = -90, max = 90)
private int longitude;

메서드에 적용하는 annotation이라면 아래코드처럼 사용하면 되겠지만 Field값에 annotation을 선언하고 그 annotation에 특정값을 주입하고 validation처리에 사용하기 위해선 java의 reflection을 활용해야 한다.

//일반적으로 메서드에 어노테이션 적용할때
@Aspect
@Component
public class MethodAspect{
    //...
    
    @Around("annotation(TEST)")
    public Object process(ProceedJointpoint jointPoint){
        //...
        Object obj=jointPoint.proceed();
        return obj;
    }
}

Example

Annotation 선언

@Target(ElementType.FIELD)//필드값에 적용할 annotation
@Retention(RetentionPolicy.RUNTIME)// 런타임에 적용될것
public @interface Validation {
    int min() default 0;
    int max() default 0;
}

위경도의 최소 최대값을 검사해줄 값들을 선언한다. 초기값은 0으로 한다.


Validation을 적용할 Class

@Component // aop를 적용하기위해 bean으로 등록필요
@NoArgsConstructor
@Getter
@Setter
public class Location {

    @Validation(min = -90, max = 90)
    private int longitude;

    @Validation(min = -180, max = 180)
    private int latitude;
    
    public void setLocation(int longitude, int latitude){
        this.longitude=longitude;
        this.latitude=latitude;
    }
}


aop적용을 위한 Aspect 작성

스크린샷_20221106_032842

@Aspect
@Component
public class ValidationAspect {
    @Around("execution(* com.example.orderdemo.aop.*.*(..))")// --> 1
    public void check(JoinPoint joinPoint) throws IllegalAccessException {
        Field[] fields = joinPoint.getTarget().getClass().getDeclaredFields(); // --> 2
        for(Field field:fields){
            field.setAccessible(true); // --> 3
            if(field.isAnnotationPresent(Validation.class)){ // --> 4
                Validation annotation=field.getAnnotation(Validation.class); // --> 5
                int a=field.getInt(joinPoint.getTarget());// --> 6
                if(annotation.min()<a&&a<annotation.max()){
                    System.out.println("범위 통과: "+field.getName()+" : "+a);
                }else{
                    System.out.println("range error : "+field.getName()+" : "+a);
                }
            }
        }
    }
}

1) Around의 execution에 (접근제어자 패키지경로.클래스명.메서드명.변수)를 작성하여 annotation이 적용될 범위를 지정한다.


2) jointPoint를 통해 execution에서 설정한 범위에 걸리는 클래스에 선언된 필드들을 가져온다.


3) private으로 선언된 Field에 접근하기 위해 true설정을 해준다 (default는 false로 되어 있다)


4) 해당 필드값에 의도한 annotation이 선언되있는지 검사


5) 해당 필드값에 선언된 annotation을 가져온다.


6) annotation이 선언된 Field의 값을 가져온다.


Test

@SpringBootTest
public class RepositoryTest{
    @Autowired
    private Location location;

    @Test
    public void customAnnotationTest() throws NoSuchFieldException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        location.setLocation(20,50);
    }
}

스크린샷_20221106_041157


@Test
public void customAnnotationTest() throws NoSuchFieldException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
    location.setLocation(2000,50);
}

스크린샷_20221106_041306


Tags:
0 comments