From d983eb8d7d86cd2086140bdbad660251e6cbccc0 Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 30 Sep 2024 16:56:38 +0900 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20JsonView=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../webmvc/servlet/view/JsonView.java | 15 +++- .../servlet/view/JsonViewStudyTest.java | 71 +++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java index 7f5eff44a9..b1cf35a6f5 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java @@ -1,14 +1,23 @@ package com.interface21.webmvc.servlet.view; -import com.interface21.webmvc.servlet.View; +import java.io.PrintWriter; +import java.util.Map; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.util.Map; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.interface21.webmvc.servlet.View; public class JsonView implements View { + private static final ObjectMapper objectMapper = new ObjectMapper(); @Override - public void render( Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + public void render(Map model, HttpServletRequest request, HttpServletResponse response) + throws Exception { + PrintWriter writer = response.getWriter(); + writer.write(objectMapper.writeValueAsString(model)); + writer.flush(); + response.setContentType("application/json"); } } diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java new file mode 100644 index 0000000000..2910b5f439 --- /dev/null +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java @@ -0,0 +1,71 @@ +package com.interface21.webmvc.servlet.view; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +class JsonViewStudyTest { + + @Test + @DisplayName("model 의 값을 JSON 으로 변환해 response 를 반환한다.") + void study() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + StringWriter stringWriter = new StringWriter(); + when(response.getWriter()).thenReturn(new PrintWriter(stringWriter)); + Map model = Map.of( + "user", new User("name", "password"), + "someModel", new SomeModel("attribute") + ); + + var jsonView = new JsonView(); + jsonView.render(model, request, response); + + assertThat(stringWriter.toString()) + .isEqualTo(new ObjectMapper().writeValueAsString(model)); + } + + private static class User { + private final String name; + private final String password; + + private User(String name, String password) { + this.name = name; + this.password = password; + } + + public String getName() { + return name; + } + + public String getPassword() { + return password; + } + } + + private static class SomeModel { + private final String attribute; + + private SomeModel(String attribute) { + this.attribute = attribute; + } + + public String getAttribute() { + return attribute; + } + } +} From 538ed8b280cdcbba1272f3bb865abbe67181c1e9 Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 30 Sep 2024 17:11:14 +0900 Subject: [PATCH 02/17] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=93=A0=20controll?= =?UTF-8?q?er=20=EB=A5=BC=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98?= =?UTF-8?q?=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ForwardController.java | 19 +++++++++ .../controller/LoginController.java | 40 ++++++++++++++----- .../controller/LoginViewController.java | 22 ---------- .../controller/LogoutController.java | 16 +++++--- .../ManualHandler/ManualHandlerMapping.java | 6 --- .../servlet/mvc/asis/ForwardController.java | 20 ---------- 6 files changed, 59 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/com/techcourse/controller/ForwardController.java delete mode 100644 app/src/main/java/com/techcourse/controller/LoginViewController.java delete mode 100644 mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/ForwardController.java diff --git a/app/src/main/java/com/techcourse/controller/ForwardController.java b/app/src/main/java/com/techcourse/controller/ForwardController.java new file mode 100644 index 0000000000..3c9ba04154 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/ForwardController.java @@ -0,0 +1,19 @@ +package com.techcourse.controller; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import com.interface21.context.stereotype.Controller; +import com.interface21.web.bind.annotation.RequestMapping; +import com.interface21.web.bind.annotation.RequestMethod; +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.view.JspView; + +@Controller +public class ForwardController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public ModelAndView index(HttpServletRequest request, HttpServletResponse response) { + return new ModelAndView(new JspView("/index.jsp")); + } +} diff --git a/app/src/main/java/com/techcourse/controller/LoginController.java b/app/src/main/java/com/techcourse/controller/LoginController.java index a87d996693..177bc2fe25 100644 --- a/app/src/main/java/com/techcourse/controller/LoginController.java +++ b/app/src/main/java/com/techcourse/controller/LoginController.java @@ -1,21 +1,28 @@ package com.techcourse.controller; -import com.techcourse.domain.User; -import com.techcourse.repository.InMemoryUserRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import com.interface21.webmvc.servlet.mvc.asis.Controller; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class LoginController implements Controller { +import com.interface21.context.stereotype.Controller; +import com.interface21.web.bind.annotation.RequestMapping; +import com.interface21.web.bind.annotation.RequestMethod; +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.view.JspView; +import com.techcourse.domain.User; +import com.techcourse.repository.InMemoryUserRepository; + +@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 { + @RequestMapping(value = "/login", method = RequestMethod.POST) + public ModelAndView save(HttpServletRequest req, HttpServletResponse res) { if (UserSession.isLoggedIn(req.getSession())) { - return "redirect:/index.jsp"; + return new ModelAndView(new JspView("redirect:/index.jsp")); } return InMemoryUserRepository.findByAccount(req.getParameter("account")) @@ -23,15 +30,26 @@ public String execute(final HttpServletRequest req, final HttpServletResponse re log.info("User : {}", user); return login(req, user); }) - .orElse("redirect:/401.jsp"); + .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")); + } + + @RequestMapping(value = "/login/view", method = RequestMethod.GET) + public ModelAndView show(HttpServletRequest req, HttpServletResponse res) { + JspView view = UserSession.getUserFrom(req.getSession()) + .map(user -> { + log.info("logged in {}", user.getAccount()); + return new JspView("redirect:/index.jsp"); + }) + .orElse(new JspView("/login.jsp")); + return new ModelAndView(view); } } diff --git a/app/src/main/java/com/techcourse/controller/LoginViewController.java b/app/src/main/java/com/techcourse/controller/LoginViewController.java deleted file mode 100644 index dc71ee8e99..0000000000 --- a/app/src/main/java/com/techcourse/controller/LoginViewController.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.techcourse.controller; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.interface21.webmvc.servlet.mvc.asis.Controller; - -public class LoginViewController implements Controller { - - private static final Logger log = LoggerFactory.getLogger(LoginViewController.class); - - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - return UserSession.getUserFrom(req.getSession()) - .map(user -> { - log.info("logged in {}", user.getAccount()); - return "redirect:/index.jsp"; - }) - .orElse("/login.jsp"); - } -} diff --git a/app/src/main/java/com/techcourse/controller/LogoutController.java b/app/src/main/java/com/techcourse/controller/LogoutController.java index 729d262de5..e95a4485ca 100644 --- a/app/src/main/java/com/techcourse/controller/LogoutController.java +++ b/app/src/main/java/com/techcourse/controller/LogoutController.java @@ -2,14 +2,20 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import com.interface21.webmvc.servlet.mvc.asis.Controller; -public class LogoutController implements Controller { +import com.interface21.context.stereotype.Controller; +import com.interface21.web.bind.annotation.RequestMapping; +import com.interface21.web.bind.annotation.RequestMethod; +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.view.JspView; - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { +@Controller +public class LogoutController { + + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public ModelAndView logout(final HttpServletRequest req, final HttpServletResponse res) throws Exception { final var session = req.getSession(); session.removeAttribute(UserSession.SESSION_KEY); - return "redirect:/"; + return new ModelAndView(new JspView("redirect:/")); } } diff --git a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java index c1dd5fc223..d636866e94 100644 --- a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java +++ b/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java @@ -1,10 +1,8 @@ package com.techcourse.framework.ManualHandler; -import com.techcourse.controller.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.interface21.webmvc.servlet.mvc.asis.Controller; -import com.interface21.webmvc.servlet.mvc.asis.ForwardController; import java.util.HashMap; import java.util.Map; @@ -16,10 +14,6 @@ public class ManualHandlerMapping { private static final Map controllers = new HashMap<>(); 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()); log.info("Initialized Handler Mapping!"); controllers.keySet() diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/ForwardController.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/ForwardController.java deleted file mode 100644 index 35cb2ab211..0000000000 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/ForwardController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.interface21.webmvc.servlet.mvc.asis; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.util.Objects; - -public class ForwardController implements Controller { - - private final String path; - - public ForwardController(String path) { - this.path = Objects.requireNonNull(path); - } - - @Override - public String execute(HttpServletRequest request, HttpServletResponse response) { - return path; - } -} From 61e888380400eb3804835a255ab5af8dc3ffe94a Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 30 Sep 2024 17:13:15 +0900 Subject: [PATCH 03/17] =?UTF-8?q?chore:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A0=88=EA=B1=B0=EC=8B=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/DispatcherServlet.java | 9 +++--- .../ManualHandler/ManualHandlerAdapter.java | 24 -------------- .../ManualHandler/ManualHandlerMapping.java | 27 ---------------- .../ManualHandlerMappingAdapter.java | 31 ------------------- .../webmvc/servlet/mvc/asis/Controller.java | 8 ----- 5 files changed, 4 insertions(+), 95 deletions(-) delete mode 100644 app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerAdapter.java delete mode 100644 app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java delete mode 100644 app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMappingAdapter.java delete mode 100644 mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/Controller.java diff --git a/app/src/main/java/com/techcourse/framework/DispatcherServlet.java b/app/src/main/java/com/techcourse/framework/DispatcherServlet.java index 29475e4aa3..3e49d69270 100644 --- a/app/src/main/java/com/techcourse/framework/DispatcherServlet.java +++ b/app/src/main/java/com/techcourse/framework/DispatcherServlet.java @@ -7,6 +7,7 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,8 +16,6 @@ import com.interface21.webmvc.servlet.mvc.tobe.HandlerMappingAdapter; import com.interface21.webmvc.servlet.mvc.tobe.NoMatchedHandlerException; import com.interface21.webmvc.servlet.mvc.tobe.annotation.AnnotationHandlerMappingAdapter; -import com.techcourse.framework.ManualHandler.ManualHandlerMapping; -import com.techcourse.framework.ManualHandler.ManualHandlerMappingAdapter; public class DispatcherServlet extends HttpServlet { @@ -31,8 +30,7 @@ public DispatcherServlet() { @Override public void init() { handlerMappingAdapters = List.of( - new AnnotationHandlerMappingAdapter("com.techcourse.controller"), - new ManualHandlerMappingAdapter(new ManualHandlerMapping()) + new AnnotationHandlerMappingAdapter("com.techcourse.controller") ); for (HandlerMappingAdapter handlerMappingAdapter : handlerMappingAdapters) { handlerMappingAdapter.initialize(); @@ -40,7 +38,8 @@ public void init() { } @Override - protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { + protected void service(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException { final String requestURI = request.getRequestURI(); log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI); diff --git a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerAdapter.java b/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerAdapter.java deleted file mode 100644 index 99cd38a559..0000000000 --- a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerAdapter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.techcourse.framework.ManualHandler; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import com.interface21.webmvc.servlet.ModelAndView; -import com.interface21.webmvc.servlet.mvc.asis.Controller; -import com.interface21.webmvc.servlet.view.JspView; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerAdapter; - -public class ManualHandlerAdapter implements HandlerAdapter { - - private final Controller controller; - - public ManualHandlerAdapter(Controller controller) { - this.controller = controller; - } - - @Override - public ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception { - String execute = controller.execute(request, response); - return new ModelAndView(new JspView(execute)); - } -} diff --git a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java deleted file mode 100644 index d636866e94..0000000000 --- a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMapping.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.techcourse.framework.ManualHandler; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.interface21.webmvc.servlet.mvc.asis.Controller; - -import java.util.HashMap; -import java.util.Map; - -public class ManualHandlerMapping { - - private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); - - private static final Map controllers = new HashMap<>(); - - public void initialize() { - - 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); - } -} diff --git a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMappingAdapter.java b/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMappingAdapter.java deleted file mode 100644 index 149bf42e3a..0000000000 --- a/app/src/main/java/com/techcourse/framework/ManualHandler/ManualHandlerMappingAdapter.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.techcourse.framework.ManualHandler; - -import jakarta.servlet.http.HttpServletRequest; - -import com.interface21.webmvc.servlet.mvc.asis.Controller; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerAdapter; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerMappingAdapter; - -public class ManualHandlerMappingAdapter implements HandlerMappingAdapter { - - private final ManualHandlerMapping manualHandlerMapping; - - public ManualHandlerMappingAdapter(ManualHandlerMapping manualHandlerMapping) { - this.manualHandlerMapping = manualHandlerMapping; - } - - @Override - public void initialize() { - manualHandlerMapping.initialize(); - } - - @Override - //todo Optional???? - public HandlerAdapter getHandler(HttpServletRequest request) { - Controller handler = manualHandlerMapping.getHandler(request.getRequestURI()); - if (handler == null) { - return null; - } - return new ManualHandlerAdapter(handler); - } -} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/Controller.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/Controller.java deleted file mode 100644 index e1883d68b8..0000000000 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/asis/Controller.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.interface21.webmvc.servlet.mvc.asis; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -public interface Controller { - String execute(HttpServletRequest req, HttpServletResponse res) throws Exception; -} From 44d1771ff66b013fd89d3cd3abfb2605caeccdea Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 30 Sep 2024 17:38:15 +0900 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + .../techcourse/controller/UserController.java | 35 +++++++++++++ .../controller/UserControllerTest.java | 49 +++++++++++++++++++ .../webmvc/servlet/view/JsonView.java | 16 +++++- .../servlet/view/JsonViewStudyTest.java | 23 +++++++-- 5 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/techcourse/controller/UserController.java create mode 100644 app/src/test/java/com/techcourse/controller/UserControllerTest.java diff --git a/app/build.gradle b/app/build.gradle index 6ce583778d..7f496f8333 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' } test { diff --git a/app/src/main/java/com/techcourse/controller/UserController.java b/app/src/main/java/com/techcourse/controller/UserController.java new file mode 100644 index 0000000000..956a249a42 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/UserController.java @@ -0,0 +1,35 @@ +package com.techcourse.controller; + + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.interface21.context.stereotype.Controller; +import com.interface21.web.bind.annotation.RequestMapping; +import com.interface21.web.bind.annotation.RequestMethod; +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.view.JsonView; +import com.techcourse.domain.User; +import com.techcourse.repository.InMemoryUserRepository; + +@Controller +public class UserController { + + private static final Logger log = LoggerFactory.getLogger(UserController.class); + + @RequestMapping(value = "/api/user", method = RequestMethod.GET) + public ModelAndView show(HttpServletRequest request, HttpServletResponse response) { + final String account = request.getParameter("account"); + log.debug("user id : {}", account); + + final ModelAndView modelAndView = new ModelAndView(new JsonView()); + final User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + + modelAndView.addObject("user", user); + return modelAndView; + } +} diff --git a/app/src/test/java/com/techcourse/controller/UserControllerTest.java b/app/src/test/java/com/techcourse/controller/UserControllerTest.java new file mode 100644 index 0000000000..31ff0609e4 --- /dev/null +++ b/app/src/test/java/com/techcourse/controller/UserControllerTest.java @@ -0,0 +1,49 @@ +package com.techcourse.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.techcourse.framework.DispatcherServlet; +import com.techcourse.repository.InMemoryUserRepository; + +class UserControllerTest { + + DispatcherServlet dispatcherServlet = new DispatcherServlet(); + + @BeforeEach + void init() { + dispatcherServlet.init(); + } + + @Test + void test() throws IOException, ServletException { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + StringWriter stringWriter = new StringWriter(); + + when(request.getRequestURI()).thenReturn("/api/user"); + when(request.getMethod()).thenReturn("GET"); + when(request.getParameter("account")).thenReturn("gugu"); + when(response.getWriter()).thenReturn(new PrintWriter(stringWriter)); + + dispatcherServlet.service(request, response); + + assertThat(stringWriter.toString()).isEqualTo( + new ObjectMapper().writeValueAsString( + InMemoryUserRepository.findByAccount("gugu").get() + )); + } +} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java index b1cf35a6f5..01e61d4ed7 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java @@ -6,6 +6,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.interface21.webmvc.servlet.View; @@ -15,9 +16,22 @@ public class JsonView implements View { @Override public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + String value = getModelAsString(model); PrintWriter writer = response.getWriter(); - writer.write(objectMapper.writeValueAsString(model)); + writer.write(value); writer.flush(); response.setContentType("application/json"); } + + private String getModelAsString(Map model) throws JsonProcessingException { + if (model.size() == 1) { + return writeSingleObjectValue(model); + } + return objectMapper.writeValueAsString(model); + } + + private String writeSingleObjectValue(Map model) throws JsonProcessingException { + String key = model.keySet().stream().findFirst().get(); + return objectMapper.writeValueAsString(model.get(key)); + } } diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java index 2910b5f439..e15afcdce5 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.PrintWriter; @@ -12,7 +11,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -22,7 +20,26 @@ class JsonViewStudyTest { @Test @DisplayName("model 의 값을 JSON 으로 변환해 response 를 반환한다.") - void study() throws Exception { + void StingObjects() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + StringWriter stringWriter = new StringWriter(); + when(response.getWriter()).thenReturn(new PrintWriter(stringWriter)); + User user = new User("name", "password"); + Map model = Map.of( + "user", user + ); + + var jsonView = new JsonView(); + jsonView.render(model, request, response); + + assertThat(stringWriter.toString()) + .isEqualTo(new ObjectMapper().writeValueAsString(user)); + } + + @Test + @DisplayName("model 값이 여러개면 Map 형식을 변환한다.") + void multipleObjects() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); StringWriter stringWriter = new StringWriter(); From f95547c058ab4621108fccc66516a119462070ad Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 30 Sep 2024 17:41:45 +0900 Subject: [PATCH 05/17] =?UTF-8?q?chore:=20tobe=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/DispatcherServletInitializer.java | 3 ++- .../controller/UserControllerTest.java | 2 +- .../servlet/mvc/{tobe => }/HandlerAdapter.java | 2 +- .../mvc/{tobe => }/HandlerExecution.java | 2 +- .../servlet/mvc/{tobe => }/HandlerKey.java | 2 +- .../mvc/{tobe => }/HandlerMappingAdapter.java | 2 +- .../{tobe => }/NoMatchedHandlerException.java | 2 +- .../annotation/AnnotationHandlerAdapter.java | 6 +++--- .../annotation/AnnotationHandlerMapping.java | 6 +++--- .../AnnotationHandlerMappingAdapter.java | 8 ++++---- .../annotation/ControllerScanner.java | 2 +- .../mvc}/framework/DispatcherServlet.java | 17 ++++++++++------- .../AnnotationHandlerMappingTest.java | 4 ++-- .../mvc}/framework/DispatcherServletTest.java | 4 ++-- 14 files changed, 33 insertions(+), 29 deletions(-) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/HandlerAdapter.java (85%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/HandlerExecution.java (96%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/HandlerKey.java (94%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/HandlerMappingAdapter.java (78%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/NoMatchedHandlerException.java (89%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/annotation/AnnotationHandlerAdapter.java (75%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/annotation/AnnotationHandlerMapping.java (93%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/annotation/AnnotationHandlerMappingAdapter.java (76%) rename mvc/src/main/java/com/interface21/webmvc/servlet/mvc/{tobe => }/annotation/ControllerScanner.java (86%) rename {app/src/main/java/com/techcourse => mvc/src/main/java/com/interface21/webmvc/servlet/mvc}/framework/DispatcherServlet.java (77%) rename mvc/src/test/java/com/interface21/webmvc/servlet/mvc/{tobe => }/AnnotationHandlerMappingTest.java (94%) rename {app/src/test/java/com/techcourse => mvc/src/test/java/com/interface21/webmvc/servlet/mvc}/framework/DispatcherServletTest.java (96%) diff --git a/app/src/main/java/com/techcourse/framework/DispatcherServletInitializer.java b/app/src/main/java/com/techcourse/framework/DispatcherServletInitializer.java index e9f8344d4e..316500ef7d 100644 --- a/app/src/main/java/com/techcourse/framework/DispatcherServletInitializer.java +++ b/app/src/main/java/com/techcourse/framework/DispatcherServletInitializer.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.interface21.web.WebApplicationInitializer; +import com.interface21.webmvc.servlet.mvc.framework.DispatcherServlet; /** * Base class for {@link WebApplicationInitializer} @@ -17,7 +18,7 @@ public class DispatcherServletInitializer implements WebApplicationInitializer { @Override public void onStartup(final ServletContext servletContext) { - final var dispatcherServlet = new DispatcherServlet(); + final var dispatcherServlet = new DispatcherServlet("com.techcourse.controller"); final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet); if (registration == null) { diff --git a/app/src/test/java/com/techcourse/controller/UserControllerTest.java b/app/src/test/java/com/techcourse/controller/UserControllerTest.java index 31ff0609e4..385199111f 100644 --- a/app/src/test/java/com/techcourse/controller/UserControllerTest.java +++ b/app/src/test/java/com/techcourse/controller/UserControllerTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.ObjectMapper; -import com.techcourse.framework.DispatcherServlet; +import com.interface21.webmvc.servlet.mvc.framework.DispatcherServlet; import com.techcourse.repository.InMemoryUserRepository; class UserControllerTest { diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java similarity index 85% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerAdapter.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java index 15c1fac4fc..809c838dd3 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerExecution.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java similarity index 96% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerExecution.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java index 2960379f40..05eac11db6 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerExecution.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerKey.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java similarity index 94% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerKey.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java index e10abd75eb..40b2e94de4 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerKey.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc; import com.interface21.web.bind.annotation.RequestMethod; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerMappingAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerMappingAdapter.java similarity index 78% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerMappingAdapter.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerMappingAdapter.java index adcd8f86dc..deb4e9cc8a 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/HandlerMappingAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerMappingAdapter.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc; import jakarta.servlet.http.HttpServletRequest; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/NoMatchedHandlerException.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/NoMatchedHandlerException.java similarity index 89% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/NoMatchedHandlerException.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/NoMatchedHandlerException.java index bc8fb12a45..6c30cea138 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/NoMatchedHandlerException.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/NoMatchedHandlerException.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc; import jakarta.servlet.http.HttpServletRequest; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java similarity index 75% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerAdapter.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java index 5cb99aa907..5fd4bd2b9e 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java @@ -1,11 +1,11 @@ -package com.interface21.webmvc.servlet.mvc.tobe.annotation; +package com.interface21.webmvc.servlet.mvc.annotation; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import com.interface21.webmvc.servlet.ModelAndView; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerAdapter; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecution; +import com.interface21.webmvc.servlet.mvc.HandlerAdapter; +import com.interface21.webmvc.servlet.mvc.HandlerExecution; public class AnnotationHandlerAdapter implements HandlerAdapter { private final HandlerExecution handlerExecution; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerMapping.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java similarity index 93% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerMapping.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java index 9fe9dbd2a7..10ca135de2 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe.annotation; +package com.interface21.webmvc.servlet.mvc.annotation; import java.lang.reflect.Method; import java.util.Arrays; @@ -13,8 +13,8 @@ import com.interface21.web.bind.annotation.RequestMapping; import com.interface21.web.bind.annotation.RequestMethod; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecution; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerKey; +import com.interface21.webmvc.servlet.mvc.HandlerExecution; +import com.interface21.webmvc.servlet.mvc.HandlerKey; public class AnnotationHandlerMapping { diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerMappingAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java similarity index 76% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerMappingAdapter.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java index 566bd50060..53707ad573 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/AnnotationHandlerMappingAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java @@ -1,10 +1,10 @@ -package com.interface21.webmvc.servlet.mvc.tobe.annotation; +package com.interface21.webmvc.servlet.mvc.annotation; import jakarta.servlet.http.HttpServletRequest; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerAdapter; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecution; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerMappingAdapter; +import com.interface21.webmvc.servlet.mvc.HandlerAdapter; +import com.interface21.webmvc.servlet.mvc.HandlerExecution; +import com.interface21.webmvc.servlet.mvc.HandlerMappingAdapter; public class AnnotationHandlerMappingAdapter implements HandlerMappingAdapter { private final AnnotationHandlerMapping annotationHandlerMapping; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/ControllerScanner.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java similarity index 86% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/ControllerScanner.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java index f4c4ed2d55..467d55c38b 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/annotation/ControllerScanner.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe.annotation; +package com.interface21.webmvc.servlet.mvc.annotation; import java.util.Set; diff --git a/app/src/main/java/com/techcourse/framework/DispatcherServlet.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java similarity index 77% rename from app/src/main/java/com/techcourse/framework/DispatcherServlet.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java index 3e49d69270..6f56a45af7 100644 --- a/app/src/main/java/com/techcourse/framework/DispatcherServlet.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java @@ -1,4 +1,4 @@ -package com.techcourse.framework; +package com.interface21.webmvc.servlet.mvc.framework; import java.util.List; import java.util.Objects; @@ -12,25 +12,28 @@ import org.slf4j.LoggerFactory; import com.interface21.webmvc.servlet.ModelAndView; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerAdapter; -import com.interface21.webmvc.servlet.mvc.tobe.HandlerMappingAdapter; -import com.interface21.webmvc.servlet.mvc.tobe.NoMatchedHandlerException; -import com.interface21.webmvc.servlet.mvc.tobe.annotation.AnnotationHandlerMappingAdapter; +import com.interface21.webmvc.servlet.mvc.HandlerAdapter; +import com.interface21.webmvc.servlet.mvc.HandlerMappingAdapter; +import com.interface21.webmvc.servlet.mvc.NoMatchedHandlerException; +import com.interface21.webmvc.servlet.mvc.annotation.AnnotationHandlerMappingAdapter; public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); + private final Object[] basePackage; + private List handlerMappingAdapters; - public DispatcherServlet() { + public DispatcherServlet(Object... basePackage) { + this.basePackage = basePackage; } @Override public void init() { handlerMappingAdapters = List.of( - new AnnotationHandlerMappingAdapter("com.techcourse.controller") + new AnnotationHandlerMappingAdapter(basePackage) ); for (HandlerMappingAdapter handlerMappingAdapter : handlerMappingAdapters) { handlerMappingAdapter.initialize(); diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java similarity index 94% rename from mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java rename to mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java index d9362ccf37..1b5da0a214 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java @@ -1,4 +1,4 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -11,7 +11,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import com.interface21.webmvc.servlet.mvc.tobe.annotation.AnnotationHandlerMapping; +import com.interface21.webmvc.servlet.mvc.annotation.AnnotationHandlerMapping; class AnnotationHandlerMappingTest { diff --git a/app/src/test/java/com/techcourse/framework/DispatcherServletTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java similarity index 96% rename from app/src/test/java/com/techcourse/framework/DispatcherServletTest.java rename to mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java index 23378884fc..1de9c86080 100644 --- a/app/src/test/java/com/techcourse/framework/DispatcherServletTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java @@ -1,4 +1,4 @@ -package com.techcourse.framework; +package com.interface21.webmvc.servlet.mvc.framework; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import com.interface21.webmvc.servlet.mvc.tobe.NoMatchedHandlerException; +import com.interface21.webmvc.servlet.mvc.NoMatchedHandlerException; class DispatcherServletTest { From 457420f5c02897c3edffa2c74489e7b2e3b99c2c Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 30 Sep 2024 17:43:38 +0900 Subject: [PATCH 06/17] =?UTF-8?q?refactor:=20model=20=EC=9D=B4=20=ED=95=98?= =?UTF-8?q?=EB=82=98=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/interface21/webmvc/servlet/view/JsonView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java index 01e61d4ed7..d03fc723bc 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java @@ -31,7 +31,12 @@ private String getModelAsString(Map model) throws JsonProcessingExcep } private String writeSingleObjectValue(Map model) throws JsonProcessingException { - String key = model.keySet().stream().findFirst().get(); + if (model.size() != 1) { + throw new IllegalStateException(); + } + String key = model.keySet().stream() + .findFirst() + .get(); return objectMapper.writeValueAsString(model.get(key)); } } From 273ad5f980b84d86e1ad01c05b4bd881cad47c3c Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 3 Oct 2024 21:20:06 +0900 Subject: [PATCH 07/17] =?UTF-8?q?style:=20dependency=20=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7f496f8333..b8b779a482 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,12 +22,12 @@ dependencies { implementation 'org.apache.tomcat.embed:tomcat-embed-jasper:10.1.25' implementation 'ch.qos.logback:logback-classic:1.5.7' implementation 'org.apache.commons:commons-lang3:3.14.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' testImplementation 'org.assertj:assertj-core:3.26.0' testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' } test { From 047876d7f408f86b39d5dea831ef725460d06a24 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 3 Oct 2024 21:55:35 +0900 Subject: [PATCH 08/17] =?UTF-8?q?refactor:=20DispatcherServlet=20=EC=9D=B4?= =?UTF-8?q?=20=ED=8F=AC=ED=95=A8=ED=95=A0=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/com/techcourse/controller/UserControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/com/techcourse/controller/UserControllerTest.java b/app/src/test/java/com/techcourse/controller/UserControllerTest.java index 385199111f..5c7d2166fa 100644 --- a/app/src/test/java/com/techcourse/controller/UserControllerTest.java +++ b/app/src/test/java/com/techcourse/controller/UserControllerTest.java @@ -21,7 +21,7 @@ class UserControllerTest { - DispatcherServlet dispatcherServlet = new DispatcherServlet(); + DispatcherServlet dispatcherServlet = new DispatcherServlet("com.techcourse.controller"); @BeforeEach void init() { From c6487f1f76f3638aab5ef9f68a973c8b47a6beff Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 3 Oct 2024 22:01:03 +0900 Subject: [PATCH 09/17] =?UTF-8?q?test:=20DispatcherServlet=20=EC=9D=B4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mvc/framework/DispatcherServletTest.java | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java index 1de9c86080..71f3b829bc 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java @@ -20,7 +20,7 @@ class DispatcherServletTest { - private final DispatcherServlet dispatcherServlet = new DispatcherServlet(); + private final DispatcherServlet dispatcherServlet = new DispatcherServlet("samples"); @BeforeEach void init() { @@ -38,22 +38,7 @@ void serviceWithAnnotation() { final var response = mock(HttpServletResponse.class); final var requestDispatcher = mock(RequestDispatcher.class); - when(request.getRequestURI()).thenReturn("/register/view"); - when(request.getMethod()).thenReturn("GET"); - when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); - - assertThatCode(() -> dispatcherServlet.service(request, response)) - .doesNotThrowAnyException(); - } - - @Test - @DisplayName("인터페이스 기반 컨트롤러 요청을 처리할 수 있다.") - void serviceWithInterface() { - final var request = mock(HttpServletRequest.class); - final var response = mock(HttpServletResponse.class); - final var requestDispatcher = mock(RequestDispatcher.class); - - when(request.getRequestURI()).thenReturn("/"); + when(request.getRequestURI()).thenReturn("/get-test"); when(request.getMethod()).thenReturn("GET"); when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); From b1ed67791819fd56a64a3187c36d6c67e4015517 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 3 Oct 2024 22:48:35 +0900 Subject: [PATCH 10/17] =?UTF-8?q?refactor:=20=EB=AF=B8=EB=94=94=EC=96=B4?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EC=9D=84=20=EC=A0=95=ED=95=B4=EC=A7=84=20?= =?UTF-8?q?=EC=83=81=EC=88=98=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/interface21/webmvc/servlet/view/JsonView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java index d03fc723bc..ff8d912c44 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.interface21.web.http.MediaType; import com.interface21.webmvc.servlet.View; public class JsonView implements View { @@ -20,7 +21,7 @@ public void render(Map model, HttpServletRequest request, HttpServlet PrintWriter writer = response.getWriter(); writer.write(value); writer.flush(); - response.setContentType("application/json"); + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); } private String getModelAsString(Map model) throws JsonProcessingException { From 6cdd040fabe173b3e18099c88e35f4e37cfccf87 Mon Sep 17 00:00:00 2001 From: zangsu Date: Thu, 3 Oct 2024 22:49:18 +0900 Subject: [PATCH 11/17] =?UTF-8?q?study:=20stage3=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/di/stage3/context/DIContainer.java | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/study/src/test/java/di/stage3/context/DIContainer.java b/study/src/test/java/di/stage3/context/DIContainer.java index b62feb1ed3..cf36c992e0 100644 --- a/study/src/test/java/di/stage3/context/DIContainer.java +++ b/study/src/test/java/di/stage3/context/DIContainer.java @@ -1,6 +1,10 @@ package di.stage3.context; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.NoSuchElementException; import java.util.Set; +import java.util.stream.Collectors; /** * 스프링의 BeanFactory, ApplicationContext에 해당되는 클래스 @@ -10,11 +14,73 @@ class DIContainer { private final Set beans; public DIContainer(final Set> classes) { - this.beans = Set.of(); + beans = classes.stream() + .map(this::createBean) + .collect(Collectors.toSet()); + beans.forEach(this::injectDependency); + beans.forEach(this::validateDependency); + } + + private Object createBean(Class aClass) { + try { + Constructor defaultConstructor = aClass.getDeclaredConstructor(); + defaultConstructor.setAccessible(true); + return defaultConstructor + .newInstance(); + } catch (Exception e) { + throw new RuntimeException("인스턴스를 생성할 수 없습니다.", e); + } + } + + private void injectDependency(Object bean) { + Field[] declaredFields = bean.getClass().getDeclaredFields(); + for (Field field : declaredFields) { + field.setAccessible(true); + if (isFieldInjected(bean, field)) { + continue; + } + field.setAccessible(true); + injectField(bean, field); + } + + } + + private boolean isFieldNull(Object bean, Field field) { + try { + return field.get(bean) == null; + } catch (IllegalAccessException e) { + throw new RuntimeException("필드를 조회할 수 없습니다."); + } + } + + private boolean isFieldInjected(Object bean, Field field) { + return !isFieldNull(bean, field); + } + + private void injectField(Object bean, Field field) { + try { + field.set(bean, getBean(field.getType())); + } catch (IllegalAccessException e) { + throw new RuntimeException("필드를 주입할 수 없습니다."); + } + } + + private void validateDependency(Object bean) { + Field[] declaredFields = bean.getClass().getDeclaredFields(); + for (Field field : declaredFields) { + field.setAccessible(true); + if (isFieldNull(bean, field)) { + throw new RuntimeException( + bean.getClass().getName() + ": " + field.getName() + " 의존성이 주입되지 않았습니다."); + } + } } @SuppressWarnings("unchecked") public T getBean(final Class aClass) { - return null; + return (T) beans.stream() + .filter(bean -> aClass.isAssignableFrom(bean.getClass())) + .findFirst() + .orElseThrow(() -> new NoSuchElementException(aClass + " 에 해당하는 빈이 존재하지 않습니다.")); } } From 3d40a74118b806047311533e0923925c30a355ae Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 16:29:39 +0900 Subject: [PATCH 12/17] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interface21/core/util/ReflectionUtils.java | 9 +++++---- .../web/SpringServletContainerInitializer.java | 1 - .../web/bind/annotation/PathVariable.java | 6 +++++- .../web/bind/annotation/RequestParam.java | 6 +++++- .../com/interface21/webmvc/servlet/View.java | 1 - .../webmvc/servlet/mvc/HandlerAdapter.java | 3 +-- .../webmvc/servlet/mvc/HandlerExecution.java | 10 +++++----- .../webmvc/servlet/mvc/HandlerKey.java | 9 ++++++--- .../annotation/AnnotationHandlerAdapter.java | 5 ++--- .../annotation/AnnotationHandlerMapping.java | 15 ++++++--------- .../AnnotationHandlerMappingAdapter.java | 3 +-- .../mvc/annotation/ControllerScanner.java | 7 +++---- .../mvc/framework/DispatcherServlet.java | 17 +++++++---------- .../webmvc/servlet/view/JsonView.java | 10 ++++------ .../webmvc/servlet/view/JspView.java | 6 +++--- .../mvc/AnnotationHandlerMappingTest.java | 4 +--- .../mvc/framework/DispatcherServletTest.java | 4 +--- .../webmvc/servlet/view/JsonViewStudyTest.java | 9 +++------ mvc/src/test/java/samples/TestController.java | 8 ++++---- 19 files changed, 62 insertions(+), 71 deletions(-) diff --git a/mvc/src/main/java/com/interface21/core/util/ReflectionUtils.java b/mvc/src/main/java/com/interface21/core/util/ReflectionUtils.java index 986da054fc..df6cac5b2d 100644 --- a/mvc/src/main/java/com/interface21/core/util/ReflectionUtils.java +++ b/mvc/src/main/java/com/interface21/core/util/ReflectionUtils.java @@ -7,7 +7,8 @@ public abstract class ReflectionUtils { /** * Obtain an accessible constructor for the given class and parameters. - * @param clazz the clazz to check + * + * @param clazz the clazz to check * @param parameterTypes the parameter types of the desired constructor * @return the constructor reference * @throws NoSuchMethodException if no such constructor exists @@ -22,9 +23,9 @@ public static Constructor accessibleConstructor(Class clazz, Class. } /** - * Make the given constructor accessible, explicitly setting it accessible - * if necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts. + * Make the given constructor accessible, explicitly setting it accessible if necessary. The + * {@code setAccessible(true)} method is only called when actually necessary, to avoid unnecessary conflicts. + * * @param ctor the constructor to make accessible * @see Constructor#setAccessible */ diff --git a/mvc/src/main/java/com/interface21/web/SpringServletContainerInitializer.java b/mvc/src/main/java/com/interface21/web/SpringServletContainerInitializer.java index da8da4f324..a26decb688 100644 --- a/mvc/src/main/java/com/interface21/web/SpringServletContainerInitializer.java +++ b/mvc/src/main/java/com/interface21/web/SpringServletContainerInitializer.java @@ -5,7 +5,6 @@ import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.HandlesTypes; - import java.util.ArrayList; import java.util.List; import java.util.Set; diff --git a/mvc/src/main/java/com/interface21/web/bind/annotation/PathVariable.java b/mvc/src/main/java/com/interface21/web/bind/annotation/PathVariable.java index 259dc6e880..165c7e453b 100644 --- a/mvc/src/main/java/com/interface21/web/bind/annotation/PathVariable.java +++ b/mvc/src/main/java/com/interface21/web/bind/annotation/PathVariable.java @@ -1,6 +1,10 @@ package com.interface21.web.bind.annotation; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) diff --git a/mvc/src/main/java/com/interface21/web/bind/annotation/RequestParam.java b/mvc/src/main/java/com/interface21/web/bind/annotation/RequestParam.java index 4db9537996..dfd0fb5190 100644 --- a/mvc/src/main/java/com/interface21/web/bind/annotation/RequestParam.java +++ b/mvc/src/main/java/com/interface21/web/bind/annotation/RequestParam.java @@ -1,6 +1,10 @@ package com.interface21.web.bind.annotation; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/View.java b/mvc/src/main/java/com/interface21/webmvc/servlet/View.java index 24d7bad00a..c81b1861df 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/View.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/View.java @@ -2,7 +2,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - import java.util.Map; public interface View { diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java index 809c838dd3..8870724798 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerAdapter.java @@ -1,10 +1,9 @@ package com.interface21.webmvc.servlet.mvc; +import com.interface21.webmvc.servlet.ModelAndView; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import com.interface21.webmvc.servlet.ModelAndView; - public interface HandlerAdapter { ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception; } diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java index 05eac11db6..d39b5ac5ae 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerExecution.java @@ -1,14 +1,13 @@ package com.interface21.webmvc.servlet.mvc; +import com.interface21.webmvc.servlet.ModelAndView; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import com.interface21.webmvc.servlet.ModelAndView; - public class HandlerExecution { private static final Map, Object> controllerInstances = new ConcurrentHashMap<>(); @@ -23,7 +22,8 @@ public HandlerExecution(Class runnerInstanceClazz, Method method) { private Object createNewInstance(Class clazz) { try { return clazz.getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { throw new RuntimeException(String.format( "컨트롤러 %s 를 생성할 수 있는 기본 생성자가 존재하지 않습니다.", clazz.getName()) ); diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java index 40b2e94de4..cf9c1af490 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/HandlerKey.java @@ -1,7 +1,6 @@ package com.interface21.webmvc.servlet.mvc; import com.interface21.web.bind.annotation.RequestMethod; - import java.util.Objects; public class HandlerKey { @@ -24,8 +23,12 @@ public String toString() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof HandlerKey)) return false; + if (this == o) { + return true; + } + if (!(o instanceof HandlerKey)) { + return false; + } HandlerKey that = (HandlerKey) o; return Objects.equals(url, that.url) && requestMethod == that.requestMethod; } diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java index 5fd4bd2b9e..0c5ca6b513 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerAdapter.java @@ -1,11 +1,10 @@ package com.interface21.webmvc.servlet.mvc.annotation; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - import com.interface21.webmvc.servlet.ModelAndView; import com.interface21.webmvc.servlet.mvc.HandlerAdapter; import com.interface21.webmvc.servlet.mvc.HandlerExecution; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; public class AnnotationHandlerAdapter implements HandlerAdapter { private final HandlerExecution handlerExecution; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java index 10ca135de2..cdf113b0f8 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMapping.java @@ -1,21 +1,18 @@ package com.interface21.webmvc.servlet.mvc.annotation; +import com.interface21.web.bind.annotation.RequestMapping; +import com.interface21.web.bind.annotation.RequestMethod; +import com.interface21.webmvc.servlet.mvc.HandlerExecution; +import com.interface21.webmvc.servlet.mvc.HandlerKey; +import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; - -import jakarta.servlet.http.HttpServletRequest; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.interface21.web.bind.annotation.RequestMapping; -import com.interface21.web.bind.annotation.RequestMethod; -import com.interface21.webmvc.servlet.mvc.HandlerExecution; -import com.interface21.webmvc.servlet.mvc.HandlerKey; - public class AnnotationHandlerMapping { private static final Logger log = LoggerFactory.getLogger(AnnotationHandlerMapping.class); @@ -56,7 +53,7 @@ private RequestMethod[] getRequestMethods(RequestMapping requestMappingAnnotatio } private void bindControllerToRequest(Class controllerClass, Method method, String requestUrl, - RequestMethod... requestMethods + RequestMethod... requestMethods ) { for (RequestMethod requestMethod : requestMethods) { handlerExecutions.put(new HandlerKey(requestUrl, requestMethod), diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java index 53707ad573..a5ae15485f 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java @@ -1,10 +1,9 @@ package com.interface21.webmvc.servlet.mvc.annotation; -import jakarta.servlet.http.HttpServletRequest; - import com.interface21.webmvc.servlet.mvc.HandlerAdapter; import com.interface21.webmvc.servlet.mvc.HandlerExecution; import com.interface21.webmvc.servlet.mvc.HandlerMappingAdapter; +import jakarta.servlet.http.HttpServletRequest; public class AnnotationHandlerMappingAdapter implements HandlerMappingAdapter { private final AnnotationHandlerMapping annotationHandlerMapping; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java index 467d55c38b..efca07ef39 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/ControllerScanner.java @@ -1,13 +1,12 @@ package com.interface21.webmvc.servlet.mvc.annotation; +import com.interface21.context.stereotype.Controller; import java.util.Set; - import org.reflections.Reflections; -import com.interface21.context.stereotype.Controller; - public class ControllerScanner { - private ControllerScanner() {} + private ControllerScanner() { + } public static Set> getControllerClass(Object... basePackage) { Reflections reflections = new Reflections(basePackage); diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java index 6f56a45af7..b785d196ce 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java @@ -1,22 +1,19 @@ package com.interface21.webmvc.servlet.mvc.framework; -import java.util.List; -import java.util.Objects; - +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.mvc.HandlerAdapter; +import com.interface21.webmvc.servlet.mvc.HandlerMappingAdapter; +import com.interface21.webmvc.servlet.mvc.NoMatchedHandlerException; +import com.interface21.webmvc.servlet.mvc.annotation.AnnotationHandlerMappingAdapter; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - +import java.util.List; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.interface21.webmvc.servlet.ModelAndView; -import com.interface21.webmvc.servlet.mvc.HandlerAdapter; -import com.interface21.webmvc.servlet.mvc.HandlerMappingAdapter; -import com.interface21.webmvc.servlet.mvc.NoMatchedHandlerException; -import com.interface21.webmvc.servlet.mvc.annotation.AnnotationHandlerMappingAdapter; - public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java index ff8d912c44..e7a66ef6bb 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JsonView.java @@ -1,15 +1,13 @@ package com.interface21.webmvc.servlet.view; -import java.io.PrintWriter; -import java.util.Map; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.interface21.web.http.MediaType; import com.interface21.webmvc.servlet.View; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.PrintWriter; +import java.util.Map; public class JsonView implements View { private static final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JspView.java b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JspView.java index 95ece5343d..6bf6d641fa 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/view/JspView.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/view/JspView.java @@ -3,11 +3,10 @@ import com.interface21.webmvc.servlet.View; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Map; - public class JspView implements View { private static final Logger log = LoggerFactory.getLogger(JspView.class); @@ -21,7 +20,8 @@ public JspView(String viewName) { } @Override - public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + public void render(Map model, HttpServletRequest request, HttpServletResponse response) + throws Exception { if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); return; diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java index 1b5da0a214..78333b0888 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/AnnotationHandlerMappingTest.java @@ -4,15 +4,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.interface21.webmvc.servlet.mvc.annotation.AnnotationHandlerMapping; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import com.interface21.webmvc.servlet.mvc.annotation.AnnotationHandlerMapping; - class AnnotationHandlerMappingTest { private AnnotationHandlerMapping handlerMapping; diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java index 71f3b829bc..fe0eaa826e 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServletTest.java @@ -6,18 +6,16 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.interface21.webmvc.servlet.mvc.NoMatchedHandlerException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import com.interface21.webmvc.servlet.mvc.NoMatchedHandlerException; - class DispatcherServletTest { private final DispatcherServlet dispatcherServlet = new DispatcherServlet("samples"); diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java index e15afcdce5..e64dd2f02e 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/view/JsonViewStudyTest.java @@ -4,18 +4,15 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Map; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.databind.ObjectMapper; - class JsonViewStudyTest { @Test diff --git a/mvc/src/test/java/samples/TestController.java b/mvc/src/test/java/samples/TestController.java index 6ad9c741dd..deef078fa5 100644 --- a/mvc/src/test/java/samples/TestController.java +++ b/mvc/src/test/java/samples/TestController.java @@ -1,14 +1,14 @@ package samples; import com.interface21.context.stereotype.Controller; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.interface21.web.bind.annotation.RequestMapping; import com.interface21.web.bind.annotation.RequestMethod; import com.interface21.webmvc.servlet.ModelAndView; import com.interface21.webmvc.servlet.view.JspView; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Controller public class TestController { From 7ca29473adf043554184802a8b8593625675761f Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 16:34:48 +0900 Subject: [PATCH 13/17] =?UTF-8?q?refactor:=20Stream=20=EC=9D=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=B4=20HandlerMappingAdapter=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EB=B6=80=ED=84=B0=20=EC=B4=88=EA=B8=B0=ED=99=94?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=EC=9D=98=20=EA=B3=BC=EC=A0=95=EC=9D=84=20?= =?UTF-8?q?=ED=95=9C=EB=B2=88=EC=97=90=20=EC=A7=84=ED=96=89=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../servlet/mvc/framework/DispatcherServlet.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java index b785d196ce..494b9c6c8b 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java @@ -11,6 +11,7 @@ import jakarta.servlet.http.HttpServletResponse; import java.util.List; import java.util.Objects; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,12 +30,11 @@ public DispatcherServlet(Object... basePackage) { @Override public void init() { - handlerMappingAdapters = List.of( - new AnnotationHandlerMappingAdapter(basePackage) - ); - for (HandlerMappingAdapter handlerMappingAdapter : handlerMappingAdapters) { - handlerMappingAdapter.initialize(); - } + handlerMappingAdapters = Stream.of(new AnnotationHandlerMappingAdapter(basePackage)) + .map(adapter -> { + adapter.initialize(); + return (HandlerMappingAdapter) adapter; + }).toList(); } @Override From f447f515af27b41dec2ac7d3135dd2b6dfd26484 Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 16:36:17 +0900 Subject: [PATCH 14/17] =?UTF-8?q?refactor:=20=EC=9A=94=EC=B2=AD=EC=9D=84?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94?= =?UTF-8?q?=20HandlerAdapter=20=EB=A5=BC=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=EC=9D=84=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../servlet/mvc/framework/DispatcherServlet.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java index 494b9c6c8b..31fcf45786 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/framework/DispatcherServlet.java @@ -44,11 +44,7 @@ protected void service(final HttpServletRequest request, final HttpServletRespon log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI); try { - HandlerAdapter handlerAdapter = handlerMappingAdapters.stream() - .map(handlerMappingAdapter -> handlerMappingAdapter.getHandler(request)) - .filter(Objects::nonNull) - .findFirst() - .orElseThrow(() -> new NoMatchedHandlerException(request)); + HandlerAdapter handlerAdapter = getHandlerAdapter(request); ModelAndView modelAndView = handlerAdapter.handle(request, response); modelAndView.getView().render(modelAndView.getModel(), request, response); } catch (Throwable e) { @@ -56,4 +52,12 @@ protected void service(final HttpServletRequest request, final HttpServletRespon throw new ServletException(e.getMessage(), e); } } + + private HandlerAdapter getHandlerAdapter(HttpServletRequest request) { + return handlerMappingAdapters.stream() + .map(handlerMappingAdapter -> handlerMappingAdapter.getHandler(request)) + .filter(Objects::nonNull) + .findFirst() + .orElseThrow(() -> new NoMatchedHandlerException(request)); + } } From 4144e07cd49a48670ddd3c8e685668574ad2b6fd Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 16:42:54 +0900 Subject: [PATCH 15/17] =?UTF-8?q?docs:=20=EC=B2=98=EB=A6=AC=ED=95=9C=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=EC=97=90=20=EB=8C=80=ED=95=9C=20todo=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java index a5ae15485f..c3dd12ab2f 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/annotation/AnnotationHandlerMappingAdapter.java @@ -19,8 +19,6 @@ public void initialize() { } @Override - //todo Optional?? - //지금은 예외가 터질거임 public HandlerAdapter getHandler(HttpServletRequest request) { HandlerExecution handlerExecution = annotationHandlerMapping.getHandler(request); if (handlerExecution == null) { From dc1697238a7585563842382654a7e031079d4ffc Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 16:59:55 +0900 Subject: [PATCH 16/17] =?UTF-8?q?refactor:=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/di/stage3/context/DIContainer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/study/src/test/java/di/stage3/context/DIContainer.java b/study/src/test/java/di/stage3/context/DIContainer.java index cf36c992e0..482bdc926c 100644 --- a/study/src/test/java/di/stage3/context/DIContainer.java +++ b/study/src/test/java/di/stage3/context/DIContainer.java @@ -39,7 +39,6 @@ private void injectDependency(Object bean) { if (isFieldInjected(bean, field)) { continue; } - field.setAccessible(true); injectField(bean, field); } From 09adbdeef0c489d6ebcc09ee4be0a91cecaecd9c Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 17:17:05 +0900 Subject: [PATCH 17/17] =?UTF-8?q?study:=20stage4=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stage4/annotations/ClassPathScanner.java | 5 +- .../di/stage4/annotations/DIContainer.java | 76 ++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/study/src/test/java/di/stage4/annotations/ClassPathScanner.java b/study/src/test/java/di/stage4/annotations/ClassPathScanner.java index 9dab1fd9c4..2dd30c9307 100644 --- a/study/src/test/java/di/stage4/annotations/ClassPathScanner.java +++ b/study/src/test/java/di/stage4/annotations/ClassPathScanner.java @@ -1,10 +1,13 @@ package di.stage4.annotations; import java.util.Set; +import java.util.stream.Collectors; +import org.junit.platform.commons.support.ReflectionSupport; public class ClassPathScanner { public static Set> getAllClassesInPackage(final String packageName) { - return null; + return ReflectionSupport.streamAllClassesInPackage(packageName, clazz -> true, name -> true) + .collect(Collectors.toSet()); } } diff --git a/study/src/test/java/di/stage4/annotations/DIContainer.java b/study/src/test/java/di/stage4/annotations/DIContainer.java index 9248ecad7e..b54ec5ae45 100644 --- a/study/src/test/java/di/stage4/annotations/DIContainer.java +++ b/study/src/test/java/di/stage4/annotations/DIContainer.java @@ -1,6 +1,12 @@ package di.stage4.annotations; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.NoSuchElementException; import java.util.Set; +import java.util.stream.Collectors; /** * 스프링의 BeanFactory, ApplicationContext에 해당되는 클래스 @@ -10,15 +16,79 @@ class DIContainer { private final Set beans; public DIContainer(final Set> classes) { - this.beans = Set.of(); + beans = classes.stream() + .map(this::createBean) + .collect(Collectors.toSet()); + beans.forEach(this::injectDependency); + beans.forEach(this::validateDependency); } public static DIContainer createContainerForPackage(final String rootPackageName) { - return null; + Set> beanClasses = ClassPathScanner.getAllClassesInPackage(rootPackageName).stream() + .filter(clazz -> clazz.isAnnotationPresent(Repository.class) || clazz.isAnnotationPresent(Service.class) +// && !clazz.isInterface() + ) + .collect(Collectors.toSet()); + return new DIContainer(beanClasses); } + private Object createBean(Class aClass) { + try { + Constructor defaultConstructor = aClass.getDeclaredConstructor(); + defaultConstructor.setAccessible(true); + return defaultConstructor + .newInstance(); + } catch (Exception e) { + throw new RuntimeException(aClass.getName() + "인스턴스를 생성할 수 없습니다.", e); + } + } + + private void injectDependency(Object bean) { + getBeanDependencies(bean) + .forEach(field -> injectField(bean, field)); + } + + private List getBeanDependencies(Object bean) { + return Arrays.stream(bean.getClass().getDeclaredFields()) + .filter(field -> field.isAnnotationPresent(Inject.class)) + .toList(); + } + + private void injectField(Object bean, Field field) { + try { + field.setAccessible(true); + field.set(bean, getBean(field.getType())); + } catch (IllegalAccessException e) { + throw new RuntimeException("필드를 주입할 수 없습니다."); + } + } + + private void validateDependency(Object bean) { + getBeanDependencies(bean).stream() + .forEach(field -> validateBeanInjection(bean, field)); + } + + private void validateBeanInjection(Object bean, Field field) { + if (isFieldNull(bean, field)) { + throw new RuntimeException(bean.getClass().getName() + ": " + field.getName() + " 의존성이 주입되지 않았습니다."); + } + } + + private boolean isFieldNull(Object bean, Field field) { + try { + field.setAccessible(true); + return field.get(bean) == null; + } catch (IllegalAccessException e) { + throw new RuntimeException("필드를 조회할 수 없습니다."); + } + } + + @SuppressWarnings("unchecked") public T getBean(final Class aClass) { - return null; + return (T) beans.stream() + .filter(bean -> aClass.isAssignableFrom(bean.getClass())) + .findFirst() + .orElseThrow(() -> new NoSuchElementException(aClass + " 에 해당하는 빈이 존재하지 않습니다.")); } }