您的位置:首页 > 娱乐 > 明星 > 对MVC的理解以及如何实现一个简单的MVC

对MVC的理解以及如何实现一个简单的MVC

2024/10/5 16:21:09 来源:https://blog.csdn.net/weixin_44050153/article/details/140135933  浏览:    关键词:对MVC的理解以及如何实现一个简单的MVC
IOC 容器与 Controller
  • 在 Spring 框架中,所有的 Controller 都会被 Spring 的 IOC 容器管理。
  • 当应用程序启动时,Spring 会扫描所有带有 @Controller 注解的类,并将它们作为 Bean 注册到 IOC 容器中。
方法扫描与 Dispatcher
  • Spring MVC 在启动时,会扫描所有 Controller 类中的方法。
  • 根据方法上的注解(例如 @RequestMapping, @GetMapping, @PostMapping 等),Spring MVC 将这些方法与特定的 URL 模式映射,并将这些映射添加到一个中央的 DispatcherServlet 中。
参数封装
  • 每个 Controller 方法的参数以及参数上的注解(如 @RequestParam, @PathVariable, @RequestBody 等)都会被封装成 Param 对象。
  • 这些 Param 对象包含了参数的类型、名称、默认值和注解类型等信息,并被存储在 methodParameters 列表中。
请求处理
  • 当一个 HTTP 请求到达时,DispatcherServlet 会根据请求的 URL 匹配到相应的处理器(Controller 方法)。
  • DispatcherServlet 调用匹配的处理器的 process 方法来处理请求。
  • process 方法中,根据请求的具体信息,调用对应的 Controller 方法。
返回结果
  • Controller 方法的返回值会被封装成一个 Result 对象,包含了处理是否成功以及处理的具体结果。
  • Result 对象会传递给视图解析器,将结果渲染成最终的视图(如 HTML、JSON 等),并返回给客户端。

实现一个简单的MVC

定义Dispatcher

    static class Dispatcher {final static Result NOT_PROCESSED = new Result(false, null);Logger logger = LoggerFactory.getLogger(getClass());boolean isRest;boolean isResponseBody;boolean isVoid;Pattern urlPattern;Object controller;Method handlerMethod;Param[] methodParameters;public Dispatcher(String httpMethod, boolean isRest, Object controller, Method method, String urlPattern) throws ServletException {this.isRest = isRest;this.isResponseBody = method.getAnnotation(ResponseBody.class) != null;this.isVoid = method.getReturnType() == void.class;this.urlPattern = Pattern.compile(urlPattern);this.controller = controller;this.handlerMethod = method;Parameter[] param = method.getParameters();Annotation[][] paramAnnos = method.getParameterAnnotations();this.methodParameters = new Param[param.length];for (int i = 0; i < param.length; i++) {methodParameters[i] = new Param(httpMethod, method, param[i], paramAnnos[i]);}}

定义Param

    static class Param {String name;ParamType paramType;Class<?> classType;String defaultValue;/*** 对参数进行解析,并设置参数类型、参数名、参数默认值** @param httpMethod* @param method* @param parameter* @param annotations* @throws ServletException*/public Param(String httpMethod, Method method, Parameter parameter, Annotation[] annotations) throws ServletException {PathVariable pv = ClassUtils.getAnnotaion(annotations, PathVariable.class);RequestParam rq = ClassUtils.getAnnotaion(annotations, RequestParam.class);RequestBody rb = ClassUtils.getAnnotaion(annotations, RequestBody.class);int total = (pv == null ? 0 : 1) + (rq == null ? 0 : 1) + (rb == null ? 0 : 1);if (total > 1) {throw new ServletException("Only one annotation can be used in a parameter." + method);}this.classType = parameter.getType();if (pv != null) {this.name = pv.value();this.paramType = ParamType.PATH_VARIABLE;} else if (rq != null) {this.name = rq.value();this.defaultValue = rq.defaultValue();this.paramType = ParamType.REQUEST_PARAM;} else if (rb != null) {this.paramType = ParamType.REQUEST_BODY;} else {this.paramType = ParamType.SERVLET_VARIABLE;if (this.classType != HttpServletRequest.class && this.classType != HttpServletResponse.class &&this.classType != HttpSession.class && this.classType != ServletContext.class) {throw new ServerErrorException("请给参数标记注解,不支持的参数类型 " + classType + " at method " + method);}}}

定义处理结果

    static record Result(boolean processed, Object returnObject) {}

初始化

   @Overridepublic void init(ServletConfig config) throws ServletException {logger.info("init{}.", getClass().getName());ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) this.applicationContext;List<BeanDefinition> beanDefinitions = configurableApplicationContext.findBeanDefinitions(Object.class);for (BeanDefinition def : beanDefinitions) {Class<?> beanClass = def.getBeanClass();Object bean = def.getRequiredInstance();Controller controller = beanClass.getAnnotation(Controller.class);RestController restController = beanClass.getAnnotation(RestController.class);if (controller != null && restController != null) {throw new ServletException("Controller and RestController can not be used at the same time." + beanClass.getName());}if (controller != null) {addController(false, def.getName(), bean);}else{addController(true, def.getName(), bean);}}}

doService

    void doService(String url, HttpServletRequest req, HttpServletResponse resp, List<Dispatcher> dispatchers) throws Exception {for (Dispatcher dispatcher : dispatchers){Result result = dispatcher.process(url, req, resp);if(result.processed()){Object r = result.returnObject();if(dispatcher.isRest){if(!resp.isCommitted()){resp.setContentType("application/json;charset=UTF-8");}if(dispatcher.isResponseBody){if(r instanceof String s){PrintWriter pw = resp.getWriter();pw.write(s);pw.flush();}else if(r instanceof byte[] data){ServletOutputStream output = resp.getOutputStream();output.write(data);output.flush();}else{throw new ServerErrorException("Unsupported return type: " + r.getClass());}}}else{if( !resp.isCommitted()){resp.setContentType("text/html");}if( r instanceof String s){if (dispatcher.isResponseBody) {// send as response body:PrintWriter pw = resp.getWriter();pw.write(s);pw.flush();} else if (s.startsWith("redirect:")) {// send redirect:resp.sendRedirect(s.substring(9));} else {// error:throw new ServletException("Unable to process String result when handle url: " + url);}} else if (r instanceof byte[] data) {if (dispatcher.isResponseBody) {// send as response body:ServletOutputStream output = resp.getOutputStream();output.write(data);output.flush();} else {// error:throw new ServletException("Unable to process byte[] result when handle url: " + url);}}else if(r instanceof ModelAndView mv){String view = mv.getViewName();if (view.startsWith("redirect:")) {// send redirect:resp.sendRedirect(view.substring(9));} else {this.viewResolver.render(view, mv.getModel(), req, resp);}} else if (!dispatcher.isVoid && r != null) {// error:throw new ServletException("Unable to process " + r.getClass().getName() + " result when handle url: " + url);}}}return;}}

详细代码地址
https://github.com/laicoffee/Spring-Summer

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com