Spring 4.3 부터 추가된거 같은 어노테이션을 커스텀하게 만들 수 있다. 흠 커스텀이라고 하는게 맞는지는 잘 모르겠지만.. meta annotation? 이 더 맞는 건가? 아무튼 나두 말로 설명을 못하겠으니 소스를 보면서 해보자.
참고로 4.2부터는 @AliasFor 어노테이션을 사용하면 가능하다. 4.3 부터는 굳이 명시를 하지 않아도 된다.

Spring 4.3부터 추가된 기능으로 @GetMapping, @PostMapping @PutMapping, @PatchMapping, @DeleteMapping등 @RequestMapping(method = RequestMethod.GET) 등을 좀더 간편하게 만들 수 있는 어노테이션이 추가되었다. 그러면서 어노테이션을 메타 어노테이션으로 설정 할 수가 있다.

어디 한번 우리도 간단하게 만들어 보자.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResponseStatus(HttpStatus.BAD_REQUEST)
public @interface ResponseStatusBadRequest {
  String reason() default "";
}

이것은 @ResponseStatus(HttpStatus.BAD_REQUEST) 을 메타 어노테이션으로 정의하였다.
그리고 아무 클래스에나 넣어 보자.

@RestController
@ResponseStatusBadRequest(reason = "badRequest hello")
public class TestController {

  @GetMapping("/hello")
  public String helloGet() {
    return "hello get";
  }

  @PostMapping("/hello")
  public String helloPost() {
    return "hello post";
  }
}

그리고 hello를 get으로 호출 하여 보면 다음과 같이 출력 될 것이다.

{
"timestamp": 1464009415655
"status": 400
"error": "Bad Request"
"message": "badRequest hello"
"path": "/hello"
}

우리는 이런식으로 커스텀하게 어노테이션을 메타 어노테이션을 사용하여 만들어 줄 수 있다.
다른 하나의 예제도 보자.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ResponseBody
@ControllerAdvice
public @interface RestControllerAdvice {
}

ResponseBody와 ControllerAdvice를 함께 사용하였다. 지금은 속성들을 제외 시켰다.
TestAdvice 클래스를 만들어서 적용해 보자.

@RestControllerAdvice
public class TestAdvice {

  @ExceptionHandler(Exception.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public String handleDuplicateKeyException(Exception e) throws IOException {
    return "exception";
  }
}

그리고 강제로 에러를 내어 보자!

@GetMapping("/hello")
public String helloGet() {
  if(true){
    throw new RuntimeException();
  }
  return "hello get";
}

그럼 exception이라고 String 문자열로 응답을 받을 것이다.
우리는 이렇게 다양하게 어노테이션을 결합하여 하나의 어노테이션으로 만들 수 있게 되었다.
여러개를 쓰면서 자주 쓰이는것은 메타 어노테이션을 만들어서 사용해보자!