diff --git a/app/src/main/java/com/techcourse/DispatcherServlet.java b/app/src/main/java/com/techcourse/DispatcherServlet.java index 277d8eed9a..8492f5e2b7 100644 --- a/app/src/main/java/com/techcourse/DispatcherServlet.java +++ b/app/src/main/java/com/techcourse/DispatcherServlet.java @@ -6,22 +6,32 @@ 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.mvc.HandlerAdapter; +import webmvc.org.springframework.web.servlet.mvc.asis.ControllerHandlerAdapter; +import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping; +import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecutionHandlerAdapter; public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); - private ManualHandlerMapping manualHandlerMapping; + private HandlerMappingRegistry handlerMappingRegistry = new HandlerMappingRegistry(); + private HandlerAdapterRegistry handlerAdapterRegistry = new HandlerAdapterRegistry(); public DispatcherServlet() { } @Override public void init() { - manualHandlerMapping = new ManualHandlerMapping(); - manualHandlerMapping.initialize(); + handlerMappingRegistry + .addHandlerMapping(new ManualHandlerMapping()) + .addHandlerMapping(new AnnotationHandlerMapping()); + handlerAdapterRegistry + .addHandlerAdapter(new ControllerHandlerAdapter()) + .addHandlerAdapter(new HandlerExecutionHandlerAdapter()); + handlerMappingRegistry.initialize(); } @Override @@ -30,22 +40,19 @@ 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 = handlerMappingRegistry.getHandler(request) + .orElseThrow(() -> new IllegalArgumentException("There is not matched handler")); + HandlerAdapter handlerAdapter = handlerAdapterRegistry.getHandlerAdapter(handler) + .orElseThrow(() -> new IllegalArgumentException("Not Supported Handler")); + ModelAndView modelAndView = handlerAdapter.handle(request, response, handler); + render(modelAndView, 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); + private void render(ModelAndView modelAndView, HttpServletRequest request, HttpServletResponse response) { + // TODO: 3단계? } } diff --git a/app/src/main/java/com/techcourse/HandlerAdapterRegistry.java b/app/src/main/java/com/techcourse/HandlerAdapterRegistry.java new file mode 100644 index 0000000000..08ef095671 --- /dev/null +++ b/app/src/main/java/com/techcourse/HandlerAdapterRegistry.java @@ -0,0 +1,26 @@ +package com.techcourse; + +import webmvc.org.springframework.web.servlet.mvc.HandlerAdapter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class HandlerAdapterRegistry { + + private final List handlerAdapters = new ArrayList<>(); + + public HandlerAdapterRegistry addHandlerAdapter(HandlerAdapter handlerAdapter) { + handlerAdapters.add(handlerAdapter); + return this; + } + + public Optional getHandlerAdapter(Object handler) { + for (HandlerAdapter handlerAdapter : handlerAdapters) { + if (handlerAdapter.supports(handler)) { + return Optional.of(handlerAdapter); + } + } + return Optional.empty(); + } +} diff --git a/app/src/main/java/com/techcourse/HandlerMappingRegistry.java b/app/src/main/java/com/techcourse/HandlerMappingRegistry.java new file mode 100644 index 0000000000..754f0496c3 --- /dev/null +++ b/app/src/main/java/com/techcourse/HandlerMappingRegistry.java @@ -0,0 +1,32 @@ +package com.techcourse; + +import jakarta.servlet.http.HttpServletRequest; +import webmvc.org.springframework.web.servlet.mvc.HandlerMapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class HandlerMappingRegistry { + + private final List handlerMappings = new ArrayList<>(); + + public HandlerMappingRegistry addHandlerMapping(HandlerMapping handlerMapping) { + handlerMappings.add(handlerMapping); + return this; + } + + public void initialize() { + handlerMappings.forEach(HandlerMapping::initialize); + } + + public Optional getHandler(HttpServletRequest httpServletRequest) { + for (HandlerMapping handlerMapping : handlerMappings) { + Object handler = handlerMapping.getHandler(httpServletRequest); + if (handler != null) { + return Optional.of(handler); + } + } + return Optional.empty(); + } +} diff --git a/app/src/main/java/com/techcourse/LegacyHandlerMapping.java b/app/src/main/java/com/techcourse/LegacyHandlerMapping.java new file mode 100644 index 0000000000..a11c629751 --- /dev/null +++ b/app/src/main/java/com/techcourse/LegacyHandlerMapping.java @@ -0,0 +1,14 @@ +package com.techcourse; + +import jakarta.servlet.http.HttpServletRequest; +import webmvc.org.springframework.web.servlet.mvc.HandlerMapping; + +public abstract class LegacyHandlerMapping implements HandlerMapping { + + @Override + public Object getHandler(HttpServletRequest httpServletRequest) { + return getHandler(httpServletRequest.getRequestURI()); + } + + public abstract Object getHandler(String requestURI); +} diff --git a/app/src/main/java/com/techcourse/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/ManualHandlerMapping.java index a54863caf8..145a6a0f24 100644 --- a/app/src/main/java/com/techcourse/ManualHandlerMapping.java +++ b/app/src/main/java/com/techcourse/ManualHandlerMapping.java @@ -9,12 +9,13 @@ import java.util.HashMap; import java.util.Map; -public class ManualHandlerMapping { +public class ManualHandlerMapping extends LegacyHandlerMapping { private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); private static final Map controllers = new HashMap<>(); + @Override public void initialize() { controllers.put("/", new ForwardController("/index.jsp")); controllers.put("/login", new LoginController()); @@ -28,6 +29,7 @@ public void initialize() { .forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass())); } + @Override public Controller getHandler(final String requestURI) { log.debug("Request Mapping Uri : {}", requestURI); return controllers.get(requestURI); diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/HandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/HandlerAdapter.java new file mode 100644 index 0000000000..049dad0e4d --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/HandlerAdapter.java @@ -0,0 +1,12 @@ +package webmvc.org.springframework.web.servlet.mvc; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; + +public interface HandlerAdapter { + + boolean supports(Object object); + + ModelAndView handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception; +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/HandlerMapping.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/HandlerMapping.java new file mode 100644 index 0000000000..917e649a5b --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/HandlerMapping.java @@ -0,0 +1,10 @@ +package webmvc.org.springframework.web.servlet.mvc; + +import jakarta.servlet.http.HttpServletRequest; + +public interface HandlerMapping { + + void initialize(); + + Object getHandler(HttpServletRequest httpServletRequest); +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ControllerHandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ControllerHandlerAdapter.java new file mode 100644 index 0000000000..f3ac39622c --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ControllerHandlerAdapter.java @@ -0,0 +1,23 @@ +package webmvc.org.springframework.web.servlet.mvc.asis; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.HandlerAdapter; +import webmvc.org.springframework.web.servlet.view.JspView; + +public class ControllerHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(Object object) { + return object instanceof Controller; + } + + @Override + public ModelAndView handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { + Controller controller = (Controller) handler; + String viewName = controller.execute(httpServletRequest, httpServletResponse); + ModelAndView mav = new ModelAndView(new JspView(viewName)); + return mav; + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java index a355218efa..26b0207a54 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java @@ -1,13 +1,18 @@ package webmvc.org.springframework.web.servlet.mvc.tobe; 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.HandlerMapping; -import java.util.HashMap; -import java.util.Map; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; -public class AnnotationHandlerMapping { +public class AnnotationHandlerMapping implements HandlerMapping { private static final Logger log = LoggerFactory.getLogger(AnnotationHandlerMapping.class); @@ -19,11 +24,60 @@ public AnnotationHandlerMapping(final Object... basePackage) { this.handlerExecutions = new HashMap<>(); } + @Override public void initialize() { - log.info("Initialized AnnotationHandlerMapping!"); + log.info("Initializing AnnotationHandlerMapping starts!"); + for (Object base : basePackage) { + ControllerScanner controllerScanner = new ControllerScanner(new Reflections(base)); + Map, Object> controllers = controllerScanner.getControllers(); + Set methods = getRequestMappingMethods(controllers.keySet()); + for (Method method : methods) { + RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); + addHandlerExecutions(controllers, method, requestMapping); + } + } + log.info("Initializing AnnotationHandlerMapping succeeds!"); } + @Override public Object getHandler(final HttpServletRequest request) { - return null; + String uri = request.getRequestURI(); + try { + RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod()); + HandlerKey handlerKey = new HandlerKey(uri, requestMethod); + return handlerExecutions.get(handlerKey); + } catch (IllegalArgumentException e) { + log.error("Unsupported request method."); + return null; + } + } + + private void addHandlerExecutions(Map, Object> controllers, Method method, RequestMapping requestMapping) { + HandlerExecution handlerExecution = mapHandlerExecution(controllers, method); + RequestMethod[] requestMethods = requestMapping.method(); + List handlerKeys = mapHandlerKeys(requestMapping.value(), requestMethods); + for (HandlerKey handlerKey : handlerKeys) { + handlerExecutions.put(handlerKey, handlerExecution); + } + } + + private HandlerExecution mapHandlerExecution(Map, Object> controllers, Method method) { + Object instance = controllers.get(method.getDeclaringClass()); + return new HandlerExecution(instance, method); + } + + private List mapHandlerKeys(String url, RequestMethod[] requestMethods) { + return Arrays.stream(requestMethods) + .map(requestMethod -> new HandlerKey(url, requestMethod)) + .collect(Collectors.toList()); + } + + private Set getRequestMappingMethods(Set> controllers) { + return controllers + .stream() + .map(Class::getMethods) + .flatMap(Arrays::stream) + .filter(method -> method.isAnnotationPresent(RequestMapping.class)) + .collect(Collectors.toSet()); } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/ControllerScanner.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/ControllerScanner.java new file mode 100644 index 0000000000..282c3c8620 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/ControllerScanner.java @@ -0,0 +1,53 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe; + +import context.org.springframework.stereotype.Controller; +import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class ControllerScanner { + + private static final Logger log = LoggerFactory.getLogger(ControllerScanner.class); + + private final Reflections reflections; + + public ControllerScanner(Reflections reflections) { + this.reflections = reflections; + } + + public Map, Object> getControllers() { + Set> controllers = reflections.getTypesAnnotatedWith(Controller.class); + return instantiateControllers(controllers); + } + + private Map, Object> instantiateControllers(Set> controllers) { + Map, Object> controllerWithInstances = new HashMap<>(); + try { + for (Class controller : controllers) { + Object instance = controller.getDeclaredConstructor().newInstance(); + controllerWithInstances.put(controller, instance); + } + return controllerWithInstances; + } catch (NoSuchMethodException e) { + log.error("NoArgConstructor doesn't exist."); + } catch (SecurityException e) { + log.error("Check permission"); + } catch (IllegalAccessException e) { + log.error("Constructor is not accessible."); + } catch (IllegalArgumentException e) { + log.error("Type of Arguments doesn't matched."); + } catch (InstantiationException e) { + log.error("The instance is abstract class."); + } catch (InvocationTargetException e) { + log.error("Exception occurs during constructing."); + } catch (ExceptionInInitializerError error) { + log.error("Initializing fails."); + } + throw new IllegalArgumentException("Getting instance using constructor fails."); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java index 37c583fbdf..8453e08ac2 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java @@ -4,9 +4,19 @@ import jakarta.servlet.http.HttpServletResponse; import webmvc.org.springframework.web.servlet.ModelAndView; +import java.lang.reflect.Method; + public class HandlerExecution { + private final Object instance; + private final Method method; + + public HandlerExecution(Object instance, Method method) { + this.instance = instance; + this.method = method; + } + public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception { - return null; + return (ModelAndView) method.invoke(instance, request, response); } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecutionHandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecutionHandlerAdapter.java new file mode 100644 index 0000000000..409dea62bf --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecutionHandlerAdapter.java @@ -0,0 +1,19 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.HandlerAdapter; + +public class HandlerExecutionHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(Object object) { + return object instanceof HandlerExecution; + } + + @Override + public ModelAndView handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { + return ((HandlerExecution) handler).handle(httpServletRequest, httpServletResponse); + } +} diff --git a/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java b/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java index dcec215a3f..56460da9c0 100644 --- a/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java +++ b/mvc/src/test/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMappingTest.java @@ -2,6 +2,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -48,4 +49,30 @@ void post() throws Exception { assertThat(modelAndView.getObject("id")).isEqualTo("gugu"); } + + @Test + void givenWrongUrl_whenGetHandler_thenReturnNull() { + final var request = mock(HttpServletRequest.class); + String wrongUrl = "/wrong-url"; + + when(request.getAttribute("id")).thenReturn("gugu"); + when(request.getRequestURI()).thenReturn(wrongUrl); + when(request.getMethod()).thenReturn("POST"); + + final var handlerExecution = (HandlerExecution) handlerMapping.getHandler(request); + Assertions.assertThat(handlerExecution).isNull(); + } + + @Test + void givenWrongRequestMethod_whenGetHandler_thenReturnNull() { + final var request = mock(HttpServletRequest.class); + String wrongRequestMethod = "PATCH"; + + when(request.getAttribute("id")).thenReturn("gugu"); + when(request.getRequestURI()).thenReturn("/post-test"); + when(request.getMethod()).thenReturn(wrongRequestMethod); + + final var handlerExecution = (HandlerExecution) handlerMapping.getHandler(request); + Assertions.assertThat(handlerExecution).isNull(); + } } diff --git a/study/src/main/java/servlet/com/example/CharacterEncodingFilter.java b/study/src/main/java/servlet/com/example/CharacterEncodingFilter.java index cf4d886974..69c730fb01 100644 --- a/study/src/main/java/servlet/com/example/CharacterEncodingFilter.java +++ b/study/src/main/java/servlet/com/example/CharacterEncodingFilter.java @@ -11,6 +11,7 @@ public class CharacterEncodingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.getServletContext().log("doFilter() 호출"); + response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } } diff --git a/study/src/test/java/reflection/Junit3TestRunner.java b/study/src/test/java/reflection/Junit3TestRunner.java index b4e465240c..720c9c94c5 100644 --- a/study/src/test/java/reflection/Junit3TestRunner.java +++ b/study/src/test/java/reflection/Junit3TestRunner.java @@ -2,6 +2,11 @@ import org.junit.jupiter.api.Test; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + class Junit3TestRunner { @Test @@ -9,5 +14,11 @@ void run() throws Exception { Class clazz = Junit3Test.class; // TODO Junit3Test에서 test로 시작하는 메소드 실행 + List methods = Arrays.stream(clazz.getDeclaredMethods()) + .filter(method -> method.getName().startsWith("test")) + .collect(Collectors.toList()); + for (Method method : methods) { + method.invoke(new Junit3Test()); + } } } diff --git a/study/src/test/java/reflection/Junit4TestRunner.java b/study/src/test/java/reflection/Junit4TestRunner.java index 8a6916bc24..3b748f4669 100644 --- a/study/src/test/java/reflection/Junit4TestRunner.java +++ b/study/src/test/java/reflection/Junit4TestRunner.java @@ -2,6 +2,11 @@ import org.junit.jupiter.api.Test; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + class Junit4TestRunner { @Test @@ -9,5 +14,11 @@ void run() throws Exception { Class clazz = Junit4Test.class; // TODO Junit4Test에서 @MyTest 애노테이션이 있는 메소드 실행 + List methods = Arrays.stream(clazz.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(MyTest.class)) + .collect(Collectors.toList()); + for (Method method : methods) { + method.invoke(new Junit4Test()); + } } } diff --git a/study/src/test/java/reflection/ReflectionTest.java b/study/src/test/java/reflection/ReflectionTest.java index 370f0932b9..0e0d45d79d 100644 --- a/study/src/test/java/reflection/ReflectionTest.java +++ b/study/src/test/java/reflection/ReflectionTest.java @@ -1,5 +1,6 @@ package reflection; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -7,9 +8,14 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.in; +import static org.assertj.core.api.InstanceOfAssertFactories.DATE; class ReflectionTest { @@ -18,26 +24,30 @@ class ReflectionTest { @Test void givenObject_whenGetsClassName_thenCorrect() { final Class clazz = Question.class; + SoftAssertions softAssertions = new SoftAssertions(); - assertThat(clazz.getSimpleName()).isEqualTo(""); - assertThat(clazz.getName()).isEqualTo(""); - assertThat(clazz.getCanonicalName()).isEqualTo(""); + softAssertions.assertThat(clazz.getSimpleName()).isEqualTo("Question"); + softAssertions.assertThat(clazz.getName()).isEqualTo("reflection.Question"); + softAssertions.assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question"); + softAssertions.assertAll(); } @Test void givenClassName_whenCreatesObject_thenCorrect() throws ClassNotFoundException { final Class clazz = Class.forName("reflection.Question"); - assertThat(clazz.getSimpleName()).isEqualTo(""); - assertThat(clazz.getName()).isEqualTo(""); - assertThat(clazz.getCanonicalName()).isEqualTo(""); + SoftAssertions softAssertions = new SoftAssertions(); + softAssertions.assertThat(clazz.getSimpleName()).isEqualTo("Question"); + softAssertions.assertThat(clazz.getName()).isEqualTo("reflection.Question"); + softAssertions.assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question"); + softAssertions.assertAll(); } @Test void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() { final Object student = new Student(); - final Field[] fields = null; - final List actualFieldNames = null; + final Field[] fields = student.getClass().getDeclaredFields(); + final List actualFieldNames = Arrays.stream(fields).map(Field::getName).collect(Collectors.toList()); assertThat(actualFieldNames).contains("name", "age"); } @@ -45,8 +55,8 @@ void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() { @Test void givenClass_whenGetsMethods_thenCorrect() { final Class animalClass = Student.class; - final Method[] methods = null; - final List actualMethods = null; + final Method[] methods = animalClass.getDeclaredMethods(); + final List actualMethods = Arrays.stream(methods).map(Method::getName).collect(Collectors.toList()); assertThat(actualMethods) .hasSize(3) @@ -56,7 +66,7 @@ void givenClass_whenGetsMethods_thenCorrect() { @Test void givenClass_whenGetsAllConstructors_thenCorrect() { final Class questionClass = Question.class; - final Constructor[] constructors = null; + final Constructor[] constructors = questionClass.getDeclaredConstructors(); assertThat(constructors).hasSize(2); } @@ -65,11 +75,21 @@ void givenClass_whenGetsAllConstructors_thenCorrect() { void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() throws Exception { final Class questionClass = Question.class; - final Constructor firstConstructor = null; - final Constructor secondConstructor = null; - - final Question firstQuestion = null; - final Question secondQuestion = null; + final Constructor firstConstructor = questionClass.getConstructor( + String.class, + String.class, + String.class); + final Constructor secondConstructor = questionClass.getConstructor( + long.class, + String.class, + String.class, + String.class, + Date.class, + int.class + ); + + final Question firstQuestion = (Question) firstConstructor.newInstance("gugu", "제목1", "내용1"); + final Question secondQuestion = (Question) firstConstructor.newInstance("gugu", "제목2", "내용2"); assertThat(firstQuestion.getWriter()).isEqualTo("gugu"); assertThat(firstQuestion.getTitle()).isEqualTo("제목1"); @@ -82,7 +102,7 @@ void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() throws Exception @Test void givenClass_whenGetsPublicFields_thenCorrect() { final Class questionClass = Question.class; - final Field[] fields = null; + final Field[] fields = questionClass.getFields(); assertThat(fields).hasSize(0); } @@ -90,7 +110,7 @@ void givenClass_whenGetsPublicFields_thenCorrect() { @Test void givenClass_whenGetsDeclaredFields_thenCorrect() { final Class questionClass = Question.class; - final Field[] fields = null; + final Field[] fields = questionClass.getDeclaredFields(); assertThat(fields).hasSize(6); assertThat(fields[0].getName()).isEqualTo("questionId"); @@ -99,7 +119,7 @@ void givenClass_whenGetsDeclaredFields_thenCorrect() { @Test void givenClass_whenGetsFieldsByName_thenCorrect() throws Exception { final Class questionClass = Question.class; - final Field field = null; + final Field field = questionClass.getDeclaredField("questionId"); assertThat(field.getName()).isEqualTo("questionId"); } @@ -107,7 +127,7 @@ void givenClass_whenGetsFieldsByName_thenCorrect() throws Exception { @Test void givenClassField_whenGetsType_thenCorrect() throws Exception { final Field field = Question.class.getDeclaredField("questionId"); - final Class fieldClass = null; + final Class fieldClass = field.getType(); assertThat(fieldClass.getSimpleName()).isEqualTo("long"); } @@ -115,15 +135,15 @@ void givenClassField_whenGetsType_thenCorrect() throws Exception { @Test void givenClassField_whenSetsAndGetsValue_thenCorrect() throws Exception { final Class studentClass = Student.class; - final Student student = null; - final Field field = null; + final Student student = (Student) studentClass.getDeclaredConstructors()[0].newInstance(); + final Field field = studentClass.getDeclaredField("age"); // todo field에 접근 할 수 있도록 만든다. - + field.setAccessible(true); assertThat(field.getInt(student)).isZero(); assertThat(student.getAge()).isZero(); - field.set(null, null); + field.set(student, 99); assertThat(field.getInt(student)).isEqualTo(99); assertThat(student.getAge()).isEqualTo(99); diff --git a/study/src/test/java/reflection/ReflectionsTest.java b/study/src/test/java/reflection/ReflectionsTest.java index 5040c2ffa2..fff5eef051 100644 --- a/study/src/test/java/reflection/ReflectionsTest.java +++ b/study/src/test/java/reflection/ReflectionsTest.java @@ -4,6 +4,9 @@ import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reflection.annotation.Controller; +import reflection.annotation.Repository; +import reflection.annotation.Service; class ReflectionsTest { @@ -14,5 +17,11 @@ void showAnnotationClass() throws Exception { Reflections reflections = new Reflections("reflection.examples"); // TODO 클래스 레벨에 @Controller, @Service, @Repository 애노테이션이 설정되어 모든 클래스 찾아 로그로 출력한다. + reflections.getTypesAnnotatedWith(Controller.class) + .forEach(clazz -> log.info(clazz.getSimpleName())); + reflections.getTypesAnnotatedWith(Service.class) + .forEach(clazz -> log.info(clazz.getSimpleName())); + reflections.getTypesAnnotatedWith(Repository.class) + .forEach(clazz -> log.info(clazz.getSimpleName())); } } diff --git a/study/src/test/java/reflection/Student.java b/study/src/test/java/reflection/Student.java index 85526b7425..1cd4a2d823 100644 --- a/study/src/test/java/reflection/Student.java +++ b/study/src/test/java/reflection/Student.java @@ -20,4 +20,4 @@ public String toString() { ", age=" + age + '}'; } -} \ No newline at end of file +} diff --git a/study/src/test/java/servlet/com/example/ServletTest.java b/study/src/test/java/servlet/com/example/ServletTest.java index 75fbb10dd5..e335f8190a 100644 --- a/study/src/test/java/servlet/com/example/ServletTest.java +++ b/study/src/test/java/servlet/com/example/ServletTest.java @@ -28,7 +28,7 @@ void testSharedCounter() { // expected를 0이 아닌 올바른 값으로 바꿔보자. // 예상한 결과가 나왔는가? 왜 이런 결과가 나왔을까? - assertThat(Integer.parseInt(response.body())).isEqualTo(0); + assertThat(Integer.parseInt(response.body())).isEqualTo(3); } @Test @@ -50,6 +50,6 @@ void testLocalCounter() { // expected를 0이 아닌 올바른 값으로 바꿔보자. // 예상한 결과가 나왔는가? 왜 이런 결과가 나왔을까? - assertThat(Integer.parseInt(response.body())).isEqualTo(0); + assertThat(Integer.parseInt(response.body())).isEqualTo(1); } }