diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandler.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandler.java new file mode 100644 index 00000000..018001c7 --- /dev/null +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandler.java @@ -0,0 +1,23 @@ +package org.example.mvc; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class AnnotationHandler { + private final Class clazz; + private final Method tagetMethod; + + public AnnotationHandler(Class clazz, Method targetMethod) { + this.clazz = clazz; + this.tagetMethod = targetMethod; + } + + public String handle(HttpServletRequest request, HttpServletResponse response) throws Exception { + Constructor declaredConstructor = clazz.getDeclaredConstructor(); + Object handler = declaredConstructor.newInstance(); + + return (String) tagetMethod.invoke(handler, request, response); + } +} diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandlerAdapter.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandlerAdapter.java new file mode 100644 index 00000000..cab1f37e --- /dev/null +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandlerAdapter.java @@ -0,0 +1,19 @@ +package org.example.mvc; + +import org.example.mvc.view.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class AnnotationHandlerAdapter implements HandlerAdapter { + @Override + public boolean supports(Object handler) { + return handler instanceof AnnotationHandler; + } + + @Override + public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String viewName = ((AnnotationHandler) handler).handle(request, response); + return new ModelAndView(viewName); + } +} diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandlerMapping.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandlerMapping.java new file mode 100644 index 00000000..fa06b988 --- /dev/null +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/AnnotationHandlerMapping.java @@ -0,0 +1,49 @@ +package org.example.mvc; + +import org.example.mvc.annotation.Controller; +import org.example.mvc.annotation.RequestMapping; +import org.example.mvc.controller.HandlerKey; +import org.example.mvc.controller.RequestMethod; +import org.reflections.Reflections; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class AnnotationHandlerMapping implements HandlerMapping { + + private final Object[] basePackage; + + private Map handlers = new HashMap<>(); + + public AnnotationHandlerMapping(Object... basePackage) { + this.basePackage = basePackage; + } + + public void init() { + Reflections reflections = new Reflections(basePackage); + + Set> classes = reflections.getTypesAnnotatedWith(Controller.class); + + classes.forEach(clazz -> { + Arrays.stream(clazz.getDeclaredMethods()).forEach(method -> { + RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class); + + Arrays.stream(getRequestMethods(requestMapping)) + .forEach(requestMethod -> handlers.put( + new HandlerKey(requestMethod, requestMapping.value()), new AnnotationHandler(clazz, method) + )); + }); + }); + } + + private RequestMethod[] getRequestMethods(RequestMapping requestMapping) { + return requestMapping.method(); + } + + @Override + public Object findHandler(HandlerKey handlerKey) { + return handlers.get(handlerKey); + } +} diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/DispatcherServlet.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/DispatcherServlet.java index f584a4b0..00b10240 100644 --- a/my-framework/mvc-practice/src/main/java/org/example/mvc/DispatcherServlet.java +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/DispatcherServlet.java @@ -1,6 +1,5 @@ package org.example.mvc; -import org.example.mvc.controller.Controller; import org.example.mvc.controller.HandlerKey; import org.example.mvc.controller.RequestMethod; import org.example.mvc.view.JspViewResolver; @@ -10,7 +9,6 @@ import org.example.mvc.view.ViewResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; @@ -18,7 +16,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.List; @WebServlet("/") @@ -26,16 +23,27 @@ public class DispatcherServlet extends HttpServlet { private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); - private RequestMappingHandlerMapping requestMappingHandlerMapping; + private List handlerMappings; private List viewResolvers; private List handlerAdapters; @Override public void init() throws ServletException { - requestMappingHandlerMapping = new RequestMappingHandlerMapping(); + RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping(); requestMappingHandlerMapping.init(); - handlerAdapters = List.of(new SimpleControllerHandlerAdaptor()); + AnnotationHandlerMapping annotationHandlerMapping = new AnnotationHandlerMapping(); + annotationHandlerMapping.init(); + + handlerMappings = List.of( + requestMappingHandlerMapping, + annotationHandlerMapping + ); + + handlerAdapters = List.of( + new SimpleControllerHandlerAdapter(), + new AnnotationHandlerAdapter() + ); viewResolvers = Collections.singletonList(new JspViewResolver()); } @@ -43,9 +51,15 @@ public class DispatcherServlet extends HttpServlet { protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { log.info("[DispatcherServlet] service started."); + String requestURI = req.getRequestURI(); + RequestMethod requestMethod = RequestMethod.valueOf(req.getMethod()); + try { - Controller handler = - requestMappingHandlerMapping.findHandler(new HandlerKey(RequestMethod.valueOf(req.getMethod()), req.getRequestURI())); + Object handler = handlerMappings.stream() + .filter(hm -> hm.findHandler(new HandlerKey(requestMethod, requestURI)) != null) + .map(hm -> hm.findHandler(new HandlerKey(requestMethod, requestURI))) + .findFirst() + .orElseThrow(() -> new ServletException("No handler for [" + requestMethod + ", " + requestURI + "]")); if (handler == null) { resp.sendError(404); diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/HandlerMapping.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/HandlerMapping.java new file mode 100644 index 00000000..28c926ef --- /dev/null +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/HandlerMapping.java @@ -0,0 +1,7 @@ +package org.example.mvc; + +import org.example.mvc.controller.HandlerKey; + +public interface HandlerMapping { + Object findHandler(HandlerKey handlerKey); +} diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/RequestMappingHandlerMapping.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/RequestMappingHandlerMapping.java index 93d617c9..7a7a99a4 100644 --- a/my-framework/mvc-practice/src/main/java/org/example/mvc/RequestMappingHandlerMapping.java +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/RequestMappingHandlerMapping.java @@ -5,12 +5,12 @@ import org.example.mvc.controller.*; import java.util.HashMap; import java.util.Map; -public class RequestMappingHandlerMapping { +public class RequestMappingHandlerMapping implements HandlerMapping { private Map mappings = new HashMap<>(); void init() { - mappings.put(new HandlerKey(RequestMethod.GET, "/"), new HomeController()); +// mappings.put(new HandlerKey(RequestMethod.GET, "/"), new HomeController()); mappings.put(new HandlerKey(RequestMethod.GET, "/users"), new UserListController()); mappings.put(new HandlerKey(RequestMethod.POST, "/users"), new UserCreateController()); mappings.put(new HandlerKey(RequestMethod.GET, "/user/form"), new ForwardController("/user/form")); diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/SimpleControllerHandlerAdaptor.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/SimpleControllerHandlerAdapter.java similarity index 90% rename from my-framework/mvc-practice/src/main/java/org/example/mvc/SimpleControllerHandlerAdaptor.java rename to my-framework/mvc-practice/src/main/java/org/example/mvc/SimpleControllerHandlerAdapter.java index 4eab71e8..2501c0a3 100644 --- a/my-framework/mvc-practice/src/main/java/org/example/mvc/SimpleControllerHandlerAdaptor.java +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/SimpleControllerHandlerAdapter.java @@ -6,7 +6,7 @@ import org.example.mvc.view.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class SimpleControllerHandlerAdaptor implements HandlerAdapter { +public class SimpleControllerHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Controller); diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/annotation/Controller.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/annotation/Controller.java new file mode 100644 index 00000000..87033939 --- /dev/null +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/annotation/Controller.java @@ -0,0 +1,11 @@ +package org.example.mvc.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Controller { +} diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/annotation/RequestMapping.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/annotation/RequestMapping.java new file mode 100644 index 00000000..9bbc62f9 --- /dev/null +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/annotation/RequestMapping.java @@ -0,0 +1,17 @@ +package org.example.mvc.annotation; + +import org.example.mvc.controller.RequestMethod; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestMapping { + + String value() default ""; + + RequestMethod[] method() default {}; +} diff --git a/my-framework/mvc-practice/src/main/java/org/example/mvc/controller/HomeController.java b/my-framework/mvc-practice/src/main/java/org/example/mvc/controller/HomeController.java index 95d28cb2..e3b3c09f 100644 --- a/my-framework/mvc-practice/src/main/java/org/example/mvc/controller/HomeController.java +++ b/my-framework/mvc-practice/src/main/java/org/example/mvc/controller/HomeController.java @@ -1,11 +1,16 @@ package org.example.mvc.controller; +import org.example.mvc.annotation.Controller; +import org.example.mvc.annotation.RequestMapping; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class HomeController implements Controller{ - @Override +@Controller +public class HomeController { + + @RequestMapping(value = "/",method = RequestMethod.GET) public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { - return "home.jsp"; + return "home"; } }