Spring Data REST #3 내부 동작
- 발표자료 Booting Spring Data REST
- Spring Data REST 예제
- Spring Data REST #1 Introduction
- Spring Data REST #2 동작 원리
- Spring Data REST #3 내부 동작
정말 Repository Interface로만 API가 동작할까? 당연히 Repository Interface만 있다고 해서 API가 동작하는 건 아니다.
우리가 Spring MVC를 사용해서 Controller를 만들어서 API를 만들듯이, Spring Data REST에는 이미 만들어진 Controller가 존재한다. 차이점이 있다면, 기존에 만들던 Controller와는 조금 다른 Spring Data REST만의 Controller를 만든다.
@RepositoryRestController
아마도 Spring MVC를 최근까지 사용해본 개발자라면 Controller를 만들기 위해서 @Controller
또는 @RestController
애노테이션을 사용해서 Controller를 만들었을 것이다.
Spring Data REST에서는 @RepositoryRestController
애노테이션을 제공한다. 기존 Controller 역할을 하는 애노테이션과 차이점이 있다면, @RepositoryRestController
는 Repository interface에서 제공되는 API를 확장하기 위한 Controller라고 말하고 싶다.
사실 Spring MVC Controller(@Controller, @RestController)와 Spring Data REST Controller(@RepositoryRestController)는 동일하게 동작한다.
Spring Data REST #2 동작 원리에서 설명했지만, Spring Data REST는 Spring MVC + Spring Data가 결합된 프로젝트다. 결국 @RepositoryRestController
도 Spring MVC에 의해 동작한다는 것이다.
Spring MVC Architecture
@RepositoryRestController
도 동일하게 동작한다는 것을 이해하기 위해서 먼저 Spring MVC가 Controller를 어떻게 찾아 실행하는지 알아야 한다.
DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 생략...
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 생략...
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 생략...
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// 생략...
}
// 생략...
}
catch (Exception ex) {
// 생략...
}
catch (Throwable err) {
// 생략...
}
finally {
// 생략...
}
}
DispatcherServlet의 일부 코드인 doDispatch 메소드이며, DispatcherServlet은 J2EE 패턴 중 하나인 FrontController패턴의 구현체다.
이 코드에서 설명하고자 하는 부분은 바로 HandlerMapping
, HandlerAdapter
다. getHandler
메소드를 호출해 HandlerExecutionChain
객체를 받고, getHandlerAdapter
메소드를 호출해 HandlerAdapter
객체를 받는다.
HandlerMapping
HandlerMapping
클래스는 우리가 만든 Controller(@Controller, @RestController, @RepositoryRestController)를 찾는 역할을한다. Spring MVC 3.1 버전 이후로 기본 HandlerMapping 구현체는 RequestMappingHandlerMapping
이 기본 HandlerMapping으로 설정된다.
HandlerAdapter
HandlerAdapter
클래스는 HandlerMapping
으로 찾은 Controller를 실행하는 역할을 한다. Spring MVC 3.1 버전 이후로 기본 HandlerAdapter 구현체는 RequestMappingHandlerAdapter
이 기본 HandlerMapping으로 설정된다.
RepositoryRestHandlerMapping, RepositoryRestHandlerAdapter
Spring Data REST에서 HandlerMapping의 구현체는 RepositoryRestHandlerMapping
이며, HandlerAdapter 구현체는 RepositoryRestHandlerAdapter
다.
그렇다면, 지금까지 설명한 이 구현체들은 어떻게 Controller를 찾을까?
RequestMappingHandlerMapping.isHandler 메소드
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
RepositoryRestHandlerMapping.isHandler 메소드
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotationUtils.findAnnotation(beanType, RepositoryRestController.class) != null;
}
HandlerMapping 구현체의 isHandler 메소드는 요청 URL에 해당하는 Controller가 있는지 체크하기 위한 메소드다. 많이 보던 애노테이션 아닌가? 애노테이션 설명은 생략하겠다.
그런데, 궁금한점이 있다. Spring Data REST를 사용하면 기존에 사용하던 Spring MVC의 Controller는 사용하지 못할까? 답은 아니다. 당연히 사용가능하다. 그 이유는 바로 DispatcherServlet
은 HandlerMapping과 HandlerAdapter를 List(List<HandlerMapping>
, List<HandlerAdapter>
)로 포함하고 있다. 때문에 요청한 URL에 해당하는 HandlerMapping을 찾아 HandlerAdapter에 전달한다.
여기까지가 @RepositoryRestController를 찾아 동작할 수 있었던 이유다.
Repository*Controller
진짜 우리가 궁금한건 어떻게 Controller를 찾는게 아니였다. Reository interface만으로 API가 동작할 수 있었던 이유가 궁금했던 것이다. 눈치 챘겠지만, Repository interface가 API로 동작 가능하도록 이미 만들어진 @RepositoryRestController가 있다.
다양한 @RepositoryRestController 구현체들이 존재한다.
- RepositoryController :
/
URL 실행 - RepositoryEntityController :
/{repository}
URL 실행 - RepositoryPropertyReferenceController :
/{repository}/{id}/{property}
URL 실행 - RepositorySearchController :
/{repository}/search/{search}
URL 실행
크게 4개 Controller가 이미 구현되어 있다.
마무리
기본 사용방법 부터 구조까지 자세히 설명하고 싶었는데, 잘 설명하지 못한거 같다. 아직 Spring Data REST를 깊게 이해하지 못한걸지도 모르겠다. 좀 더 공부하며 어디에, 어떻게 적용하면 좋을지 고민해봐야겠다. 추가로 궁금한 부분 질문 또는 토론이 필요하다면 언제든지 환영합니다.
출처 : https://blog.woniper.net/347?category=699184
'Spring Framework > Spring boot' 카테고리의 다른 글
Spring Data JPA 같은 이름, 다른 type인 2개의 @Entity인 경우 주의 사항 (0) | 2020.09.04 |
---|---|
Spring Batch의 동작 코드 #Step 생성과 실행 (0) | 2020.09.04 |
Spring Batch의 동작 코드 #Job 생성과 실행1 (0) | 2020.09.04 |
누구나 아는 Spring Batch 기본 개념 (0) | 2020.09.04 |
Spring Data REST #2 동작 원리 (0) | 2020.09.04 |
Spring Data REST #1 Introduction (0) | 2020.09.04 |
Spring *Utils Classes 배끼기 (0) | 2020.09.04 |
prototype bean은 정말 항상 새로운 객체를 반환할까? (0) | 2020.09.04 |