首页IT科技springmvc工作原理最简单的概述(day08-SpringMVC底层机制简单实现-04)

springmvc工作原理最简单的概述(day08-SpringMVC底层机制简单实现-04)

时间2025-04-29 07:59:40分类IT科技浏览5248
导读:SpringMVC底层机制简单实现-04 https://github.com/liyuelian/springmvc-demo.git...

SpringMVC底层机制简单实现-04

https://github.com/liyuelian/springmvc-demo.git

8.任务7-完成简单视图解析

功能说明:通过目标方法返回的 String           ,转发或重定向到指定页面

8.1分析

原生的 SpringMVC 使用视图解析器来对 Handler 方法返回的 String(该String会转为视图类)进行解析                  ,然后转发或重定向到指定页面           。

这里为了简化      ,直接在自定义的前端控制器编写方法完成视图解析器的功能                  。

8.2代码实现

(1)修改 MyDispatcherServlet 的 executeDispatch 方法

部分代码:

//编写方法        ,完成分发请求 private void executeDispatch(HttpServletRequest request, HttpServletResponse response) { MyHandler myHandler = getMyHandler(request); try { //如果 myHandler为 null                 ,说明请求 url没有匹配的方法         ,即用户请求的资源不存在 if (myHandler == null) { response.getWriter().print("<h1>404 NOT FOUND</h1>"); } else {//匹配成功,就反射调用控制器的方法 //1.先获取目标方法的所有形参的参数信息 Class<?>[] parameterTypes = myHandler.getMethod().getParameterTypes(); //2.创建一个参数数组(对应实参数组)      ,在后面反射调动目标方法时会用到 Object[] params = new Object[parameterTypes.length]; //遍历形参数组 parameterTypes                 ,根据形参数组的信息            ,将实参填充到实参数组中 //步骤一:将方法的 request 和 response 参数封装到参数数组   ,进行反射调用 for (int i = 0; i < parameterTypes.length; i++) { //.... //....略 //.... } //步骤二:将 http请求的参数封装到 params数组中[要注意填充实参数组的顺序问题] //先处理中文乱码问题 request.setCharacterEncoding("utf-8"); Map<String, String[]> parameterMap = request.getParameterMap(); // 遍历 parameterMap                 ,将请求参数按照顺序填充到实参数组 params for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { //.... //....略 //.... } //反射调用目标方法 Object result = myHandler.getMethod() .invoke(myHandler.getController(), params); //对返回的结果进行解析(原生的SpringMVC通过视图解析器来完成) if (result instanceof String) { String viewName = (String) result; System.out.println("viewName=" + viewName); if (viewName.contains(":")) {//如果返回的String结果为 forward:/login_ok.jsp // 或 redirect:/login_ok.jsp 的形式 String viewType = viewName.split(":")[0]; // forward或redirect String viewPage = viewName.split(":")[1]; // 要跳转的页面名 //判断是 forward 还是 redirect if ("forward".equals(viewType)) {//请求转发 request.getRequestDispatcher(viewPage) .forward(request, response); } else if ("redirect".equals(viewType)) {//重定向 //注意这里的路径问题 viewPage = request.getContextPath() + viewPage; response.sendRedirect(viewPage); } } else {//如果两者都没有               ,默认为请求转发 request.getRequestDispatcher("/" + viewName) .forward(request, response); } }//这里还可以拓展 } } catch (Exception e) { e.printStackTrace(); } }

(2)创建测试页面和测试方法

MonsterService 接口:

package com.li.service; import com.li.entity.Monster; import java.util.List; /** * @author 李 * @version 1.0 */ public interface MonsterService { //增加方法,处理登录 public boolean login(String name); }

MonsterServiceImpl 实现类:

package com.li.service.impl; import com.li.entity.Monster; import com.li.myspringmvc.annotation.Service; import com.li.service.MonsterService; import java.util.ArrayList; import java.util.List; /** * @author 李 * @version 1.0 * MonsterServiceImpl 作为一个Service对象注入容器 */ @Service public class MonsterServiceImpl implements MonsterService { @Override public boolean login(String name) { //模拟DB if ("白骨精".equals(name)) { return true; } else { return false; } } }

MonsterController 控制器:

package com.li.controller; import com.li.entity.Monster; import com.li.myspringmvc.annotation.AutoWired; import com.li.myspringmvc.annotation.Controller; import com.li.myspringmvc.annotation.RequestMapping; import com.li.myspringmvc.annotation.RequestParam; import com.li.service.MonsterService; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.List; /** * @author 李 * @version 1.0 * 用于测试的 Controller */ @Controller public class MonsterController { //属性 @AutoWired private MonsterService monsterService; //处理登录的方法,返回要请求转发或重定向的字符串 @RequestMapping(value = "/monster/login") public String login(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "monsterName") String mName) { System.out.println("----接收到的mName-->" + mName); request.setAttribute("mName", mName); boolean b = monsterService.login(mName); if (b) {//登录成功 // 请求转发到login_ok.jsp //return "forward:/login_ok.jsp"; //return "redirect:/login_ok.jsp"; return "login_ok.jsp"; } else {//登录失败 //return "forward:/login_error.jsp"; //return "redirect:/login_error.jsp"; return "login_error.jsp"; } } }

在webapp目录下分别创建 login.jsp              ,login_ok.jsp                  ,login_error.jsp

login.jsp:

<%-- Created by IntelliJ IDEA. User: li Date: 2023/2/12 Time: 22:24 Version: 1.0 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录页面</title> </head> <body> <h1>登录页面</h1> <form action="monster/login" method="post"> 妖怪名:<input type="text" name="monsterName"><br/> <input type="submit" value="登录"> </form> </body> </html>

login_ok.jsp:

<%-- Created by IntelliJ IDEA. User: li Date: 2023/2/12 Time: 22:27 Version: 1.0 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>登录成功</title> </head> <body> <h1>登录成功</h1> 欢迎你:${requestScope.mName} </body> </html>

login_error.jsp:

<%-- Created by IntelliJ IDEA. User: li Date: 2023/2/12 Time: 22:28 Version: 1.0 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>登录失败</title> </head> <body> <h1>登录失败</h1> sorry,登录失败 ${requestScope.mName} </body> </html>

(3)启动 tomcat   ,访问 http://localhost:8080/li_springmvc/login.jsp

测试成功      。

9.任务8-自定义@ResponseBody

9.1分析

功能说明:通过自定义@ResponseBody 注解           ,返回 JSON格式数据

在实际开发中                  ,前后端分离的项目      ,通常是直接json数据给客户端/浏览器        。客户端接收到数据后        ,再自己决定如何处理和显示                 。

9.2代码实现

(1)@ResponseBody 注解

package com.li.myspringmvc.annotation; import java.lang.annotation.*; /** * @author 李 * @version 1.0 * ResponseBody 注解用于指定目标方法是否要返回指定格式的数据 * 如果value为默认值                 ,或者value="json"         ,认为目标方法要返回的数据格式为json */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ResponseBody { String value() default ""; }

(2)修改 MyDispatcherServlet 的 executeDispatch 方法

//编写方法      ,完成分发请求 private void executeDispatch(HttpServletRequest request, HttpServletResponse response) { MyHandler myHandler = getMyHandler(request); try { //如果 myHandler为 null                 ,说明请求 url没有匹配的方法            ,即用户请求的资源不存在 if (myHandler == null) { response.getWriter().print("<h1>404 NOT FOUND</h1>"); } else {//匹配成功,就反射调用控制器的方法 Class<?>[] parameterTypes = myHandler.getMethod().getParameterTypes(); //2.创建一个参数数组(对应实参数组)   ,在后面反射调动目标方法时会用到 Object[] params = new Object[parameterTypes.length]; //遍历形参数组 parameterTypes                 ,根据形参数组的信息               ,将实参填充到实参数组中 //... //... //... //... //反射调用目标方法 Object result = myHandler.getMethod().invoke(myHandler.getController(), params); //对返回的结果进行解析(原生的SpringMVC通过视图解析器来完成) if (result instanceof String) { //....略 }//这里还可以拓展 else if (result instanceof ArrayList) {//如果是一个集合 Method method = myHandler.getMethod(); //判断目标方法是否有一个@ResponseBody注解 if (method.isAnnotationPresent(ResponseBody.class)) { String valueType = method.getAnnotation(ResponseBody.class).value(); //如果注解的为默认值,或者value="json"              ,就认为目标方法要返回的数据格式为json if ("json".equals(valueType) || "".equals(valueType)) { //对Arraylist转为json字符串 //这里我们使用jackson包下的工具类解决 ObjectMapper objectMapper = new ObjectMapper(); String resultJson = objectMapper.writeValueAsString(result); //这里简单处理                  ,就直接返回 response.setContentType("text/html;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(resultJson); writer.flush(); writer.close(); } } } } } catch (Exception e) { e.printStackTrace(); } }

(3)pom.xml文件中引入jackson

<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.4</version> </dependency>

(4)MonsterController 测试类增加方法测试

/** * 编写方法   ,返回json格式的数据 * 1.目标方法返回的结果是给SpringMVC底层通过反射调用的位置 * 2.我们在SpringMVC底层反射调用的位置接收到结果并进行解析即可 * 3. @ResponseBody(value = "json") 表示希望以json格式返回数据给浏览器 * @param request * @param response * @return */ @RequestMapping(value = "/monster/list/json") @ResponseBody(value = "json") public List<Monster> listMonsterByJson(HttpServletRequest request, HttpServletResponse response) { List<Monster> monsters = monsterService.listMonster(); return monsters; }

(5)启动 tomcat           ,浏览器访问 http://localhost:8080/li_springmvc/monster/list/json                  ,返回如下结果      ,测试成功         。

10.小结

SpringMVC机制梳理

web.xml 中配置前端控制器(DispatcherServlet)和 spring 容器文件

当启动 tomcat 时        ,DispatcherServlet 被 tomcat 创建

前端控制器工作:

(1)创建 spring 容器并初始化(从 web.xml 文件中获取 spring配置文件名):

a. 扫描包                 ,获取要注入的类的全路径      。

b. 将扫描到的类进行反射         ,放入ioc容器                 。

c. 完成属性自动装配

(2)记录控制器的目标方法和 url 的映射关系(在原生 SpringMVC 中      ,这个工作由 HandlerMapping 完成)

(3)完成分发请求:

a. 完成用户 url 和控制器 url 的匹配以及目标方法的调用

b. 目标方法参数的自动赋值:对浏览器请求 url 的参数进行处理                 ,考虑目标方法形参的多样性            ,将其封装到参数数组   ,以反射调用的形式传递给目标方法

目标方法的实参是在 SpringMVC 底层通过封装好的参数数组传入的

c. 反射目标方法                 ,对目标方法返回的结果进行解析(原生SpringMVC中               ,解析的工作由视图解析器完成),决定请求转发/重定向/返回 json 格式的数据等

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
如何判断网络SEO优化周期(从排名、网站流量、用户转化三方面入手) 云主机经常打不开怎么办呀怎么解决(云主机经常打不开怎么办?)