Skip to content

Commit

Permalink
[MVC 구현하기 - 2단계] 하마드(이건회) 미션 제출합니다! ⚽️ (#505)
Browse files Browse the repository at this point in the history
* refactor : 인스턴스화시 예외처리 런타임으로 변환

* refactor : RequestMethod 변환 기능 내장함수로 대체

* feat : 어노테이션 기반 컨트롤러 공존

* refactor : swtich-case 기반 정적메소드 변경

* feat : 로그인, 회원가입 컨트롤러 어노테이션 기반으로 처리
  • Loading branch information
rawfishthelgh authored Sep 22, 2023
1 parent e888c2e commit d64fa12
Show file tree
Hide file tree
Showing 17 changed files with 399 additions and 85 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation "org.apache.tomcat.embed:tomcat-embed-jasper:10.1.13"
implementation "ch.qos.logback:logback-classic:1.2.12"
implementation "org.apache.commons:commons-lang3:3.13.0"
implementation "org.reflections:reflections:0.10.2"

testImplementation "org.assertj:assertj-core:3.24.2"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.2"
Expand Down
61 changes: 43 additions & 18 deletions app/src/main/java/com/techcourse/DispatcherServlet.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
package com.techcourse;

import com.techcourse.handlerMapper.AnnotationHandlerAdapter;
import com.techcourse.handlerMapper.AnnotationHandlerMapping;
import com.techcourse.handlerMapper.HandlerAdapter;
import com.techcourse.handlerMapper.HandlerMapping;
import com.techcourse.handlerMapper.ManualHandlerAdapter;
import com.techcourse.handlerMapper.ManualHandlerMapping;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.view.JspView;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.View;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class DispatcherServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);

private ManualHandlerMapping manualHandlerMapping;
private List<HandlerMapping> handlerMappings = new ArrayList<>();
private List<HandlerAdapter> handlerAdapters = new ArrayList<>();

public DispatcherServlet() {
}

@Override
public void init() {
manualHandlerMapping = new ManualHandlerMapping();
manualHandlerMapping.initialize();
initHandlerMapping();
initHandlerAdapter();
}

private void initHandlerMapping() {
handlerMappings.add(new ManualHandlerMapping());
handlerMappings.add(new AnnotationHandlerMapping("com.techcourse.controller"));
for (HandlerMapping handlerMapping : handlerMappings) {
handlerMapping.initialize();
}
}

private void initHandlerAdapter() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new ManualHandlerAdapter());
}

@Override
Expand All @@ -30,22 +54,23 @@ protected void service(final HttpServletRequest request, final HttpServletRespon
log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI);

try {
final var controller = manualHandlerMapping.getHandler(requestURI);
final var viewName = controller.execute(request, response);
move(viewName, request, response);
Object handler = handlerMappings.stream()
.map(handlerMapping -> handlerMapping.getHandler(request))
.filter(Objects::nonNull)
.findFirst()
.orElseThrow(() -> new RuntimeException());

HandlerAdapter handlerAdapter = handlerAdapters.stream()
.filter(adapter -> adapter.supports(handler))
.findAny()
.orElseThrow(() -> new RuntimeException());

ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);
View view = modelAndView.getView();
view.render(modelAndView.getModel(), request, response);
} catch (Throwable e) {
log.error("Exception : {}", e.getMessage(), e);
throw new ServletException(e.getMessage());
}
}

private void move(final String viewName, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
if (viewName.startsWith(JspView.REDIRECT_PREFIX)) {
response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length()));
return;
}

final var requestDispatcher = request.getRequestDispatcher(viewName);
requestDispatcher.forward(request, response);
}
}
43 changes: 24 additions & 19 deletions app/src/main/java/com/techcourse/controller/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,41 @@

import com.techcourse.domain.User;
import com.techcourse.repository.InMemoryUserRepository;
import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class LoginController implements Controller {
@Controller
public class LoginController {

private static final Logger log = LoggerFactory.getLogger(LoginController.class);

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
if (UserSession.isLoggedIn(req.getSession())) {
return "redirect:/index.jsp";
@RequestMapping(value = "/login/view", method = RequestMethod.GET)
public ModelAndView showPage(HttpServletRequest request, HttpServletResponse response) {
if (UserSession.isLoggedIn(request.getSession())) {
return new ModelAndView(new JspView("redirect:/index.jsp"));
}
return new ModelAndView(new JspView("/login.jsp"));
}

return InMemoryUserRepository.findByAccount(req.getParameter("account"))
.map(user -> {
log.info("User : {}", user);
return login(req, user);
})
.orElse("redirect:/401.jsp");
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) {
if (UserSession.isLoggedIn(request.getSession())) {
return new ModelAndView(new JspView("redirect:/index.jsp"));
}
return InMemoryUserRepository.findByAccount(request.getParameter("account"))
.map(user -> login(request, user))
.orElse(new ModelAndView(new JspView("redirect:/401.jsp")));
}

private String login(final HttpServletRequest request, final User user) {
private ModelAndView login(final HttpServletRequest request, final User user) {
if (user.checkPassword(request.getParameter("password"))) {
final var session = request.getSession();
session.setAttribute(UserSession.SESSION_KEY, user);
return "redirect:/index.jsp";
return new ModelAndView(new JspView("redirect:/index.jsp"));
}
return "redirect:/401.jsp";
return new ModelAndView(new JspView("redirect:/401.jsp"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@

import com.techcourse.domain.User;
import com.techcourse.repository.InMemoryUserRepository;
import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class RegisterController implements Controller {
@Controller
public class RegisterController {

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
@RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView register(HttpServletRequest request, HttpServletResponse response) {
final var user = new User(2,
req.getParameter("account"),
req.getParameter("password"),
req.getParameter("email"));
request.getParameter("account"),
request.getParameter("password"),
request.getParameter("email"));
InMemoryUserRepository.save(user);
return new ModelAndView(new JspView("redirect:/index.jsp"));
}

return "redirect:/index.jsp";
@RequestMapping(value = "/register/view", method = RequestMethod.GET)
public ModelAndView showPage(HttpServletRequest request, HttpServletResponse response) {
return new ModelAndView(new JspView("/register.jsp"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.techcourse.handlerMapper;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecution;

public class AnnotationHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerExecution;
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((HandlerExecution) handler).handle(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.techcourse.handlerMapper;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecution;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerKey;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationHandlerMapping implements HandlerMapping {

private static final Logger log = LoggerFactory.getLogger(AnnotationHandlerMapping.class);

private final Object[] basePackage;
private final Map<HandlerKey, HandlerExecution> handlerExecutions;

public AnnotationHandlerMapping(final Object... basePackage) {
this.basePackage = basePackage;
this.handlerExecutions = new HashMap<>();
}

@Override
public void initialize() {
Reflections reflections = new Reflections(basePackage);
Set<Class<?>> controllers = reflections.getTypesAnnotatedWith(Controller.class);
for (Class<?> controller : controllers) {
Method[] methods = controller.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
String url = annotation.value();
for (RequestMethod requestMethod : annotation.method()) {
Object instance = null;
try {
instance = controller.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
HandlerKey handlerKey = new HandlerKey(url, requestMethod);
HandlerExecution handlerExecution = new HandlerExecution(instance, method);
handlerExecutions.put(handlerKey, handlerExecution);
}
}
}
}
log.info("Initialized AnnotationHandlerMapping!");
}

@Override
public Object getHandler(final HttpServletRequest request) {
HandlerKey handlerKey = new HandlerKey(request.getRequestURI(), RequestMethod.resolve(request.getMethod()));
return handlerExecutions.get(handlerKey);
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/com/techcourse/handlerMapper/HandlerAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.techcourse.handlerMapper;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.ModelAndView;

public interface HandlerAdapter {

boolean supports(Object handler);

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/techcourse/handlerMapper/HandlerMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.techcourse.handlerMapper;

import jakarta.servlet.http.HttpServletRequest;

public interface HandlerMapping {

void initialize();

Object getHandler(HttpServletRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.techcourse.handlerMapper;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import webmvc.org.springframework.web.servlet.view.JspView;

public class ManualHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof Controller;
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String viewName = ((Controller) handler).execute(request, response);
return new ModelAndView(new JspView(viewName));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.techcourse;
package com.techcourse.handlerMapper;

import com.techcourse.controller.*;
import com.techcourse.controller.LogoutController;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
Expand All @@ -9,27 +10,25 @@
import java.util.HashMap;
import java.util.Map;

public class ManualHandlerMapping {
public class ManualHandlerMapping implements HandlerMapping {

private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class);

private static final Map<String, Controller> controllers = new HashMap<>();

@Override
public void initialize() {
controllers.put("/", new ForwardController("/index.jsp"));
controllers.put("/login", new LoginController());
controllers.put("/login/view", new LoginViewController());
controllers.put("/logout", new LogoutController());
controllers.put("/register/view", new RegisterViewController());
controllers.put("/register", new RegisterController());

log.info("Initialized Handler Mapping!");
controllers.keySet()
.forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass()));
}

public Controller getHandler(final String requestURI) {
log.debug("Request Mapping Uri : {}", requestURI);
return controllers.get(requestURI);
@Override
public Controller getHandler(HttpServletRequest request) {
log.debug("Request Mapping Uri : {}", request.getRequestURI());
return controllers.get(request.getRequestURI());
}
}
Loading

0 comments on commit d64fa12

Please sign in to comment.