Skip to content

Commit 3591039

Browse files
committed
feat: MVC 프레임워크 만들기 [step2]
1 parent d80a232 commit 3591039

11 files changed

+155
-14
lines changed

Diff for: src/main/java/org/example/mvc/AnnotationHandler.java

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.example.mvc;
2+
3+
import javax.servlet.http.HttpServletRequest;
4+
import javax.servlet.http.HttpServletResponse;
5+
import java.lang.reflect.Constructor;
6+
import java.lang.reflect.Method;
7+
8+
public class AnnotationHandler {
9+
private final Class<?> clazz;
10+
private final Method targetMethod;
11+
12+
public AnnotationHandler(Class<?> clazz, Method targetMethod) {
13+
this.clazz = clazz;
14+
this.targetMethod = targetMethod;
15+
}
16+
17+
public String handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
18+
Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();
19+
Object targetObject = defaultConstructor.newInstance();
20+
21+
return (String) targetMethod.invoke(targetObject, request, response);
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.example.mvc;
2+
3+
import org.example.mvc.view.ModelAndView;
4+
5+
import javax.servlet.http.HttpServletRequest;
6+
import javax.servlet.http.HttpServletResponse;
7+
8+
public class AnnotationHandlerAdapter implements HandlerAdapter {
9+
@Override
10+
public boolean supports(Object handler) {
11+
return handler instanceof AnnotationHandler;
12+
}
13+
14+
@Override
15+
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
16+
throws Exception {
17+
String viewName = ((AnnotationHandler) handler).handle(request, response);
18+
return new ModelAndView(viewName);
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.example.mvc;
2+
3+
import org.example.mvc.annotation.RequestMapping;
4+
import org.example.mvc.controller.RequestMethod;
5+
import org.reflections.Reflections;
6+
7+
import java.util.Arrays;
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
import java.util.Set;
11+
12+
public class AnnotationHandlerMapping implements HandlerMapping{
13+
private final Object[] basePackage;
14+
15+
private final Map<HandlerKey, AnnotationHandler> handlers = new HashMap<>();
16+
17+
public AnnotationHandlerMapping(Object... basePackage) {
18+
this.basePackage = basePackage;
19+
}
20+
21+
public void initialize() {
22+
Reflections reflections = new Reflections(basePackage);
23+
24+
Set<Class<?>> clazzesWithControllerAnnotation = reflections.getTypesAnnotatedWith(org.example.mvc.annotation.Controller.class, true);
25+
26+
clazzesWithControllerAnnotation.forEach(clazz ->
27+
28+
Arrays.stream(clazz.getDeclaredMethods()).forEach(declaredMethod -> {
29+
RequestMapping requestMappingAnnotation = declaredMethod.getDeclaredAnnotation(RequestMapping.class);
30+
31+
Arrays.stream(getRequestMethods(requestMappingAnnotation))
32+
.forEach(requestMethod -> handlers.put(
33+
new HandlerKey(requestMappingAnnotation.value(), requestMethod), new AnnotationHandler(clazz, declaredMethod)
34+
));
35+
36+
})
37+
);
38+
}
39+
40+
private RequestMethod[] getRequestMethods(RequestMapping requestMappingAnnotation) {
41+
return requestMappingAnnotation.method();
42+
}
43+
44+
@Override
45+
public Object findHandler(HandlerKey handlerKey) {
46+
return handlers.get(handlerKey);
47+
}
48+
}

Diff for: src/main/java/org/example/mvc/DispatcherServlet.java

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.example.mvc;
22

3-
import org.example.mvc.controller.Controller;
43
import org.example.mvc.controller.RequestMethod;
54
import org.example.mvc.view.JspViewResolver;
65
import org.example.mvc.view.ModelAndView;
@@ -21,24 +20,35 @@
2120
public class DispatcherServlet extends HttpServlet {
2221
private static final Logger logger = LoggerFactory.getLogger(DispatcherServlet.class);
2322

24-
private RequestMappingHandlerMapping handlerMapping;
23+
private List<HandlerMapping> handlerMappings;
2524

2625
private List<HandlerAdapter> handlerAdapters;
2726

2827
private List<ViewResolver> viewResolvers;
2928

3029
@Override
3130
public void init() {
32-
handlerMapping = new RequestMappingHandlerMapping();
33-
handlerMapping.init();
31+
RequestMappingHandlerMapping rmhm = new RequestMappingHandlerMapping();
32+
rmhm.init();
3433

35-
handlerAdapters = Collections.singletonList(new SimpleControllerHandlerAdapter());
34+
AnnotationHandlerMapping ahm = new AnnotationHandlerMapping("org.example");
35+
ahm.initialize();
36+
37+
handlerMappings = List.of(rmhm, ahm);
38+
handlerAdapters = List.of(new SimpleControllerHandlerAdapter(), new AnnotationHandlerAdapter());
3639
viewResolvers = Collections.singletonList(new JspViewResolver());
3740
}
3841

3942
@Override
4043
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
41-
Controller controller = handlerMapping.findHandler(new HandlerKey(request.getRequestURI(), RequestMethod.valueOf(request.getMethod())));
44+
String requestURI = request.getRequestURI();
45+
RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod());
46+
47+
Object controller = handlerMappings.stream()
48+
.filter(hm -> hm.findHandler(new HandlerKey(requestURI, requestMethod)) != null)
49+
.map(hm -> hm.findHandler(new HandlerKey(requestURI, requestMethod)))
50+
.findFirst()
51+
.orElseThrow(() -> new ServletException("No handler for [" + requestMethod + ", " + requestURI + "]"));
4252

4353
try {
4454
HandlerAdapter handlerAdapter = handlerAdapters.stream()

Diff for: src/main/java/org/example/mvc/HandlerKey.java

+8
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ public boolean equals(Object o) {
2525
public int hashCode() {
2626
return Objects.hash(url, requestMethod);
2727
}
28+
29+
@Override
30+
public String toString() {
31+
return "HandlerKey{" +
32+
"url='" + url + '\'' +
33+
", requestMethod=" + requestMethod +
34+
'}';
35+
}
2836
}

Diff for: src/main/java/org/example/mvc/HandlerMapping.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.example.mvc;
22

3-
import org.example.mvc.controller.Controller;
4-
53
public interface HandlerMapping {
6-
Controller findHandler(HandlerKey handlerKey);
4+
Object findHandler(HandlerKey handlerKey);
75
}

Diff for: src/main/java/org/example/mvc/RequestMappingHandlerMapping.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class RequestMappingHandlerMapping implements HandlerMapping {
1212
private Map<HandlerKey, Controller> mappings = new HashMap<>();
1313

1414
void init() {
15-
mappings.put(new HandlerKey("/", RequestMethod.GET), new HomeController());
15+
// mappings.put(new HandlerKey("/", RequestMethod.GET), new HomeController());
1616
mappings.put(new HandlerKey("/user/form", RequestMethod.GET), new ForwardController("/user/form"));
1717
mappings.put(new HandlerKey("/users", RequestMethod.GET), new UserListController());
1818
mappings.put(new HandlerKey("/users", RequestMethod.POST), new UserCreateController());
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.example.mvc.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target({ElementType.TYPE})
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface Controller {
11+
String value() default "";
12+
13+
String path() default "";
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
package org.example.mvc.annotation;
3+
4+
import org.example.mvc.controller.RequestMethod;
5+
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
11+
@Target({ElementType.METHOD, ElementType.TYPE})
12+
@Retention(RetentionPolicy.RUNTIME)
13+
public @interface RequestMapping {
14+
String value() default "";
15+
16+
RequestMethod[] method() default {};
17+
}

Diff for: src/main/java/org/example/mvc/controller/HomeController.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package org.example.mvc.controller;
22

3+
import org.example.mvc.annotation.Controller;
4+
import org.example.mvc.annotation.RequestMapping;
35
import org.example.mvc.repository.UserRepository;
46

57
import javax.servlet.http.HttpServletRequest;
68
import javax.servlet.http.HttpServletResponse;
79

8-
public class HomeController implements Controller {
9-
@Override
10+
@Controller
11+
public class HomeController {
12+
@RequestMapping(value = "/", method = RequestMethod.GET)
1013
public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
1114
request.setAttribute("users", UserRepository.findAll());
1215
return "home";

Diff for: webapps/user/list.jsp

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
<thead>
1414
<tr>
1515
<th>#</th>
16-
<th>userId</th>
17-
<th>name</th>
16+
<th>아이디</th>
17+
<th>이름</th>
1818
<th></th>
1919
</tr>
2020
</thead>

0 commit comments

Comments
 (0)