@WebServlet(name="frontControllerServletV1",urlPatterns = "/front-controller/v1/*" )
// /front-controller/v1를 포함한 어떤 하위 url이 들어와도 이 서블릿이 무조건 호출
public class FrontControllerServletV1 extends HttpServlet {
private Map<String, ControllerV1> controllerMap = new HashMap<>();
// 키는 URL > 어떤 URL이 호출이 되면 컨트롤러 V1을 꺼내서 호출해라
public FrontControllerServletV1() {
controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FrontControllerServletV1.service");
String requestURI = request.getRequestURI();
// url의 /front-controller/v1/~ 이부분을 그대로 받음
ControllerV1 controller = controllerMap.get(requestURI);
//map에서 해당 requestURI에 해당하는 컨트롤러 꺼내기(다형성에 의해서 인터페이스로 꺼낼 수 있음)
//값에 있는 컨트롤러들이 다 ControllerV1 인터페이스를 구현한 객체이기 때문에
if(controller==null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
//잘못된 URL이여서 해당하는 컨트롤러가 없는경우 404 error
return;
}
controller.process(request,response);
//해당 컨트롤러 실행
}
}
FrontControllerServletV2 .java
@WebServlet(name="frontControllerServletV2",urlPatterns = "/front-controller/v2/*" )
public class FrontControllerServletV2 extends HttpServlet {
...
//프론트 컨트롤러에서 view.render()를 호출하는 공통 로직 처리
MyView view = controller.process(request,response);
view.render(request,response);
}
}
MyView.java
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
//뷰가 렌더링 되도록 동작
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request,response);
}
}
/WEB-INF/views/new-form.jsp
>> new-form
/WEB-INF/views/save-result.jsp
>> save-result
/WEB-INF/views/members.jsp
>> members
- viewResolver : 뷰의 논리 이름을 실제 물리 이름으로 바꿔줌
ModelView.java
V3을 개발자 입장에서 좀 더 쉽게 만들기
V3과 차이점
한가지 인터페이스를 고정해서 사용하는 것이 아니라 여러가지 컨트롤러 인터페이스를 사용해서 개발하고 싶을 때 > 어댑터 패턴 사용
ControllerV3HandlerAdapter.java
// 어댑터의 역할
// 핸들러를 호출해주고 그 결과가 오면 그 반환타입을 맞춰서 반환해줘야함
public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
@Override
//핸들러가 넘어왔을 때 이 핸들러를 처리할 수 있는지 ? 판단
public boolean supports(Object handler) {
return (handler instanceof ControllerV3);
//ControllerV3를 구현한 무언가가 넘어오게 되면 참을 반환
//나머지는 false 반환
}
@Override
//실제 컨트롤러를 전달받고 이 어댑터를 통해서만 컨트롤러를 호출하고, 그 결과로 ModelView를 반환
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
ControllerV3 controller = (ControllerV3) handler;
//유연하기위해 Object로 되어있던 handler를 타입변환(캐스팅)
//supports메서드로 걸러진 애를 찾아서 그다음에 handle을 호출하기 때문에 캐스팅해도 됨
Map<String,String> paramMap=createParamMap(request);
ModelView mv=controller.process(paramMap);
return mv;
}
private Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName,
request.getParameter(paramName)));
return paramMap;
}
}
FrontControllerServletV5.java
@WebServlet(name="frontControllerServletV5",urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
private final Map<String,Object> handlerMappingMap=new HashMap<>();
//기존에 어댑터를 사용하지 않던 코드에는 Object 말고 구체적인 인터페이스 타입이 들어갔었음
//버전 상관없이 아무 컨트롤러나 다 지원하기위해서 Object 사용
private final List<MyHandlerAdapter> handlerAdapters=new ArrayList<>();
public FrontControllerServletV5() {
initHandlerMappingMap();
initHandlerAdapters();
}
private void initHandlerMappingMap() {
handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());
}
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//매핑 정보를 가지고 핸들러 맵에서 찾음
Object handler=getHandler(request);
if(handler==null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
//핸들러 어댑터 찾음
MyHandlerAdapter adapter = getHandlerAdapter(handler);
ModelView mv = adapter.handle(request,response,handler);
String viewName = mv.getViewName(); //논리이름
MyView view=viewResolver(viewName);//논리이름을 물리이름으로 바꿔줌
view.render(mv.getModel(),request,response);
}
private Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI();
return handlerMappingMap.get(requestURI);
}
private MyHandlerAdapter getHandlerAdapter(Object handler) {
for (MyHandlerAdapter adapter : handlerAdapters) {
if(adapter.supports(handler)){
return adapter;
}
}
throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler="+handler);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}