Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MVC 미션 1단계] 오리(오현서) 미션 제출합니다. #355

Merged
merged 4 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package webmvc.org.springframework.web.servlet.mvc.tobe;

import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.stream.Collectors.toList;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
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;

public class AnnotationHandlerMapping {

Expand All @@ -20,10 +31,55 @@ public AnnotationHandlerMapping(final Object... basePackage) {
}

public void initialize() {
for (Object packagePath : basePackage) {
initHandlerExecutions(packagePath);
}
log.info("Initialized AnnotationHandlerMapping!");
}

public Object getHandler(final HttpServletRequest request) {
return null;
private void initHandlerExecutions(Object packagePath) {
Set<Class<?>> clazz = getClazzByAnnotation(packagePath, Controller.class);
for (Class<?> aClass : clazz) {
List<Method> methods = getMethodsByAnnotation(aClass, RequestMapping.class);
Object instance = getInstance(aClass);
Comment on lines +41 to +44

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 분리가 이해하기 쉽게 잘 되어 있어서 좋네요 👍👍

for (Method method : methods) {
HandlerExecution handlerExecution = new HandlerExecution(instance, method);
putHandlerExecution(handlerExecution, method);
}
}
}

private <T extends Annotation> Set<Class<?>> getClazzByAnnotation(Object packagePath, Class<T> annotationClass) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드명을 getClassByAnnotation이라고 해도 좋을 것 같아요!

Reflections reflections = new Reflections(packagePath);
return reflections.getTypesAnnotatedWith(annotationClass);
}

private <T extends Annotation> List<Method> getMethodsByAnnotation(Class<?> aClass, Class<T> annotationClass) {
return Arrays.stream(aClass.getDeclaredMethods())
.filter(it -> it.isAnnotationPresent(annotationClass))
.collect(toList());
}

private Object getInstance(Class<?> aClass) {
try {
return aClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e
) {
throw new HandlerMappingException();
}
}

private void putHandlerExecution(HandlerExecution handlerExecution, Method method) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
for (RequestMethod requestMethod : requestMapping.method()) {
HandlerKey handlerKey = new HandlerKey(requestMapping.value(), requestMethod);
handlerExecutions.put(handlerKey, handlerExecution);
}
}
Comment on lines +74 to +79

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestMapping 어노테이션을 보면 method 디폴트가 빈 배열이여서 만약 RequestMapping을 사용하는 개발자가 실수로 method를 지정해주지 않는다면 해당 메서드는 등록되지 않겠네요!
이런 경우 get할 때 null을 반환하네요. 이에 대한 예외 처리도 해주면 좋을 것 같은데 어떻게 생각하시나요??

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아! 추가적으로 위와 같은 맥락으로 getHandler()를 호출 했을 때 해당하는 핸들러가 없을 때 null을 반환하기 때문에 외부에서 handlerExecution.handle(request, response); 을 할 때 NPE가 뜨게 되는데 null을 반환하는게 좋을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀해주신 부분을 놓치고 있었네요!! 다음 단계 들어가면서 관련 로직을 추가해보겠습니다


public HandlerExecution getHandler(final HttpServletRequest request) {
HandlerKey handlerKey = new HandlerKey(request.getRequestURI(), RequestMethod.valueOf(request.getMethod()));
return handlerExecutions.get(handlerKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import webmvc.org.springframework.web.servlet.ModelAndView;

public class HandlerExecution {

private Object target;
private Method method;

public HandlerExecution(Object target, Method method) {
this.target = target;
this.method = method;
}

public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
return null;
return (ModelAndView) method.invoke(target, request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package webmvc.org.springframework.web.servlet.mvc.tobe;

public class HandlerMappingException extends RuntimeException {


}
Loading