当前位置: 首页 > news >正文

网站建设的整个过程/论坛排名

网站建设的整个过程,论坛排名,老板让我做网站负责人,互联网大会官网手动实现SpringMVC底层机制-下 实现任务阶段五🍍完成Spring容器对象的自动装配-Autowired 实现任务阶段六🍍完成控制器方法获取参数-RequestParam1.🥦将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用2.&a…

手动实现SpringMVC底层机制-下

  • 实现任务阶段五
    • 🍍完成Spring容器对象的自动装配-@Autowired
  • 实现任务阶段六
    • 🍍完成控制器方法获取参数-@RequestParam
      • 1.🥦将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用
      • 2.🥦在方法形参处, 指定 @RequestParam, 将对应的实参封装到参数数组, 进行反射调用
      • 3.🥦在方法形参 没有指定 @RequestParam, 按照默认参数名获取值, 进行反射调用
  • 实现任务阶段七
    • 🍍完成简单视图解析
  • 实现任务阶段八
    • 🍍完成返回JSON格式数据-@ResponseBody
      • 🥦分析+代码实现
      • 🥦完成测试

在本篇文章中,我们将继续深入探讨如何手动实现SpringMVC的底层机制。通过这两部分的学习,你将全面理解SpringMVC的工作原理。


⬅️ 上一讲: SpringMVC系列七: 手动实现SpringMVC底层机制-上



🔧 需要用到的项目: zzw-springmvc项目


在这里插入图片描述

实现任务阶段五

🍍完成Spring容器对象的自动装配-@Autowired

说明: 完成Spring容器中对象的注入/自动装配

示意图[分析说明]

在这里插入图片描述

分析:
加入@Autowired注解, 进行对象属性的装配. -如图
在这里插入图片描述

测试:
浏览器输入 http://localhost:8080/monster/list, 返回列表信息
在这里插入图片描述

代码实现:
1.在com.zzw.zzwspringmvc.annotation下新建@Autowired

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {String value() default "";
}

1.1MonsterController添加属性monsterService, 标注@Autowired

@Controller
public class MonsterController {//@Autowired表示要完成属性的装配.@Autowiredprivate MonsterService monsterService;.....
}

2.ZzwWebApplicationContext增加方法executeAutowired()

//编写方法, 完成属性的自动装配
public void executeAutowired() {//判断ioc有没有要装配的对象if (ioc.isEmpty()) {return;//你也可以抛出异常, throw new RuntimeException("ioc 容器没有bean对象")}//遍历ioc容器中的所有注入的bean对象, 然后获取到bean的所有字段/属性, 判断是否需要装配/*** entry => <String, Object> -> String 就是你注入对象时的名称, Object就是bean对象*/for (Map.Entry<String, Object> entry : ioc.entrySet()) {//String key = entry.getKey();Object bean = entry.getValue();//获取bean的所有字段/属性Field[] declaredFields = bean.getClass().getDeclaredFields();for (Field declaredField : declaredFields) {//判断当前这个字段, 是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//有@Autowired//得到当前这个字段的@AutowiredAutowired autowiredAnnotation = declaredField.getDeclaredAnnotation(Autowired.class);String beanName = autowiredAnnotation.value();if ("".equals(beanName)) {//如果没有设置value, 按照默认规则//即得到字段类型名称的首字母小写, 作为名字来进行装配Class<?> type = declaredField.getType();beanName = type.getSimpleName().substring(0, 1).toLowerCase()+ type.getSimpleName().substring(1);}//如果设置了value, 直接按照beanName来装配//从ioc容器中获取beanif (ioc.get(beanName) == null) {//说明你指定的名字对应的bean不在ioc容器throw new RuntimeException("ioc容器中, 不存在你要装配的bean");}//防止属性是private, 我们需要暴力破解declaredField.setAccessible(true);try {declaredField.set(bean, ioc.get(beanName));} catch (Exception e) {throw new RuntimeException(e);}}}}
}

3.ZzwWebApplicationContext.javainit()方法的最后添加三行代码

//编写方法, 完成自己的spring容器的初始化
public void init() {//这里我们写的是固定的spring容器配置文件 => 做活//String basePackage = XMLParser.getBasePackage("zzwspringmvc.xml");String basePackage =XMLParser.getBasePackage(configLocation.split(":")[1]);//这时basePackages => com.zzw.controller, com.zzw.serviceString[] basePackages = basePackage.split(",");if (basePackages.length > 0) {for (String pack : basePackages) {scanPackage(pack.trim());}}System.out.println("basePackage=" + basePackage);System.out.println("classFullPathList=" + classFullPathList);//将扫描到的类, 反射到ioc容器executeInstance();System.out.println("扫描后 ioc容器=" + ioc);//完成注入的bean对象,的属性的装配executeAutowired();System.out.println("装配后 ioc容器=" + ioc);

3.打断点, debug, 重启tomcat

在这里插入图片描述
在这里插入图片描述

4.修改MonsterControllerlistMonster方法

@RequestMapping(value = "/monster/list")
public void listMonster(HttpServletRequest request, HttpServletResponse response) {//设置返回编码和返回类型response.setContentType("text/html;charset=utf-8");StringBuilder content = new StringBuilder("<h1>妖怪列表信息</h1>");//单线程使用StringBuildercontent.append("<table border='1px' width='500px' style='border-collapse:collapse'>");//调用monsterServiceList<Monster> monsters = monsterService.listMonster();for (Monster monster : monsters) {content.append("<tr><td>" + monster.getId() + "</td><td>" + monster.getName() +"</td><td>" + monster.getAge() + "</td><td>" + monster.getSkill() + "</td></tr>");}content.append("</table>");//获取writer返回信息try {response.getWriter().write(content.toString());} catch (IOException e) {throw new RuntimeException(e);}
}

5.重启tomcat, 浏览器输入 http://localhost:8080/monster/list
在这里插入图片描述

实现任务阶段六

🍍完成控制器方法获取参数-@RequestParam

功能说明: 自定义@RequestParam 和 方法参数名获取参数
完成: 将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用
完成: 在方法参数 指定 @RequestParam 的参数封装到参数数组, 进行反射调用
完成: 在方法参数 没有指定 @RequestParam, 按照默认参数名获取值, 进行反射调用

示意图[分析说明]
在这里插入图片描述

1.🥦将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用

修改ZzwDispatcherServletexecuteDispatcher()方法

//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//说明用户请求的路径/资源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射调用控制器的方法//目标将: HttpServletRequest 和 HttpServletResponse 封装到参数数组//1. 得到目标方法的所有形参参数信息[返回对应的数组]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时, 会使用到Object[] params = new Object[parameterTypes.length];//3.遍历parameterTypes形参数组, 根据形参数组信息, 将实参填充到实参数组中for (int i = 0; i < parameterTypes.length; i++) {//取出每一个形参"类型"Class<?> parameterType = parameterTypes[i];//如果这个形参类型是HttpServletRequest, 将request填充到params//在原生的SpringMVC中, 是按照类型来进行匹配的, 老师这里简化, 使用名字来匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if("HttpServletResponse".equals(parameterType.getSimpleName())){params[i] = response;}}/*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参 => 封装到参数数组*   => 然后以反射调用的方式传递给目标方法* 源码: public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}

2.🥦在方法形参处, 指定 @RequestParam, 将对应的实参封装到参数数组, 进行反射调用

1.MonsterService新添方法findMonsterByName

public interface MonsterService {//增加方法, 通过传入的name, 返回对应的monster列表public List<Monster> findMonsterByName(String name);
}

MonsterServiceImpl将其实现

@Service
public class MonsterServiceImpl implements MonsterService {public List<Monster> findMonsterByName(String name) {//这里我们模拟数据->DBList<Monster> monsters = new ArrayList<Monster>();monsters.add(new Monster(100, "牛魔王", "芭蕉扇", 400));monsters.add(new Monster(200, "汤姆猫", "抓老鼠", 200));monsters.add(new Monster(300, "红孩儿", "三昧真火", 100));monsters.add(new Monster(400, "黄袍怪", "吐烟雾", 300));monsters.add(new Monster(500, "白骨精", "美人计", 800));//创建集合并且返回查询到的monster集合List<Monster> findMonsters = new ArrayList<Monster>();//遍历monsters集合, 返回满足条件的对象for (Monster monster : monsters) {if (monster.getName().contains(name)) {findMonsters.add(monster);}}return findMonsters;}
}

2.com.zzw.zzwspringmvc.annotation下新建@RequestParam

/*** @author 赵志伟* @version 1.0* RequestParam 注解 标注在目标方法的参数上, 表示对应http请求的参数*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)//runtime表示在反射时可以拿到这个注解
@Documented
public @interface RequestParam {String value() default "";
}

3.ZzwDispatcherServlet增添代码

注意点
1)method.getParameters(): 得到所有的形参
2)method.getParameterTypes(): 得到形参的类型

//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//说明用户请求的路径/资源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射调用控制器的方法//目标将: HttpServletRequest 和 HttpServletResponse 封装到参数数组//1. 得到目标方法的所有形参参数信息[对应的数组]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时, 会使用到Object[] params = new Object[parameterTypes.length];//3.遍历parameterTypes形参数组, 根据形参数组信息, 将实参填充到实参数组中for (int i = 0; i < parameterTypes.length; i++) {//取出每一个形参类型Class<?> parameterType = parameterTypes[i];//如果这个形参是HttpServletRequest, 将request填充到params//在原生的SpringMVC中, 是按照类型来进行匹配的, 老师这里简化, 使用名字来匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {params[i] = response;}}//将http请求参数封装到params数组中, 老韩提示, 要注意填充实参的时候, 顺序问题 👈//1.获取http请求的参数集合//老韩解读//2.返回的Map<String, String[]> String: 表示http请求的参数名//  String[]: 表示http请求的参数值, 想一下为什么是数组?//http://localhost:8080/monster/find?name=牛魔王&hobby=打篮球&hobby=喝酒&hobby=吃肉(防止有类似checkbox)Map<String, String[]> parameterMap =request.getParameterMap();//3.遍历parameterMap, 将请求参数, 按照顺序填充到实参数组for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {//取出key. 这个name就是对应请求的参数名String name = entry.getKey();//说明: 只考虑提交的数据是单值的情况, 即不考虑类似checkbox提交的数据//     老师这里做了简化, 如果考虑多值情况, 也不难String value = entry.getValue()[0];//我们得到请求的参数对应我们目标方法的第几个形参, 然后将其填充//这里专门编写一个方法, 得到请求参数对应的是第几个形参//1. API 2.java内力真正增加 3.老韩忠告..int indexRequestParameterIndx =getIndexRequestParameterIndex(zzwHandler.getMethod(), name);if (indexRequestParameterIndx != -1) {//找到对应的位置params[indexRequestParameterIndx] = value;} else {//说明并没有找到@RequestParam注解对应的参数, 就会使用默认的机制进行匹配[待...]//一会再写}}                                                                 👈/*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参=> 封装到参数数组=> 然后以反射调用的方式传递给目标方法* public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}//编写方法, 返回请求参数是目标方法的第几个形参/*** @param method 目标方法* @param name   请求的参数名* @return 是目标方法的第几个形参*/
public int getIndexRequestParameterIndex(Method method, String name) {//1.得到method的所有形参参数Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {//取出当前的参数Parameter parameter = parameters[i];//判断parameter是不是有@RequestParam注解boolean annotationPresent = parameter.isAnnotationPresent(RequestParam.class);if (annotationPresent) {//说明有@RequestParam//取出当前这个参数的@RequestParam(value = "xxx")RequestParam requestParamAnnotation =parameter.getAnnotation(RequestParam.class);String value = requestParamAnnotation.value();//这里就是匹配的比较if (name.equals(value)) {return i;//找到请求的参数, 对应的目标方法的形参的位置}}}//如果没有匹配成功, 就返回-1return -1;
}

4.MonsterController增加如下方法

//增加方法, 通过name返回对应的monster对象
@RequestMapping(value = "/monster/find")
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,@RequestParam(value = "name") String name) {//设置返回编码和返回类型response.setContentType("text/html;charset=utf8");System.out.println("---接收到的name---" + name);👈StringBuilder content = new StringBuilder("<h1>妖怪列表信息</h1>");//单线程使用StringBuildercontent.append("<table border='1px' width='500px' style='border-collapse:collapse'>");//调用monsterServiceList<Monster> monsters = monsterService.findMonsterByName(name);👈for (Monster monster : monsters) {content.append("<tr><td>" + monster.getId() + "</td><td>" + monster.getName() + "</td><td>"+ monster.getAge() + "</td><td>" + monster.getSkill() + "</td></tr>");}content.append("</table>");//获取writer返回信息try {response.getWriter().write(content.toString());} catch (IOException e) {throw new RuntimeException(e);}
}

5.测试
在这里插入图片描述在这里插入图片描述

3.🥦在方法形参 没有指定 @RequestParam, 按照默认参数名获取值, 进行反射调用

1.去掉MonsterControllerfindMonsterByName()方法的name字段的@RequestParam注解

//增加方法, 通过name返回对应的monster对象
@RequestMapping(value = "/monster/find")
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,String name) {.....
}

2.ZzwDispatcherServletexecuteDispatcher()else分支内增加如下代码, 并增加方法getParameterNames

//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//说明用户请求的路径/资源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射调用控制器的方法//目标将: HttpServletRequest 和 HttpServletResponse 封装到参数数组//1. 得到目标方法的所有形参参数信息[对应的数组]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时, 会使用到Object[] params = new Object[parameterTypes.length];//3.遍历parameterTypes形参数组, 根据形参数组信息, 将实参填充到实参数组中for (int i = 0; i < parameterTypes.length; i++) {//取出每一个形参类型Class<?> parameterType = parameterTypes[i];//如果这个形参是HttpServletRequest, 将request填充到params//在原生的SpringMVC中, 是按照类型来进行匹配的, 老师这里简化, 使用名字来匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {params[i] = response;}}//将http请求参数封装到params数组中, 老韩提示, 要注意填充实参的时候, 顺序问题//1.获取http请求的参数集合//老韩解读//http://localhost:8080/monster/find?name=牛魔王&hobby=打篮球&hobby=喝酒&hobby=吃肉(防止有类似checkbox)//2.返回的Map<String, String[]> String: 表示http请求的参数名//  String[]: 表示http请求的参数值, 为什么是数组//Map<String, String[]> parameterMap = request.getParameterMap();//2.遍历parameterMap, 将请求参数, 按照顺序填充到实参数组for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {//取出key. 这个name就是对应请求的参数名String name = entry.getKey();//说明: 只考虑提交的数据是单值的情况, 即不考虑类似checkbox提交的数据//     老师这里做了简化, 如果考虑多值情况, 也不难String value = entry.getValue()[0];//我们得到请求的参数对应我们目标方法的第几个形参, 然后将其填充//这里专门编写一个方法, 得到请求参数对应的是第几个形参//1. API 2.java内力真正增加 3.老韩忠告..int indexRequestParameterIndx =getIndexRequestParameterIndex(zzwHandler.getMethod(), name);if (indexRequestParameterIndx != -1) {//找到对应的位置params[indexRequestParameterIndx] = value;
👉👉👉👉👉👉👉} else {//说明并没有找到@RequestParam注解对应的参数, 就会使用默认的机制进行配置[待...]//思路 														//1.得到目标方法的所有形参的名称, 而不是形参类型的名称-专门编写一个方法获取形参名//2.对得到的目标方法的所有形参名进行遍历, 如果匹配就把当前请求的参数值, 填充到paramsList<String> parameterNames = getParameterNames(zzwHandler.getMethod());for (int i = 0; i < parameterNames.size(); i++) {//如果请求参数名和目标方法的形参名一样, 说明匹配成功if (name.equals(parameterNames.get(i))) {params[i] = value;//填充到实参数组break;}}                  									}}/*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参=> 封装到参数数组=> 然后以反射调用的方式传递给目标方法* public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}//编写方法, 得到目标方法的所有形参的名称, 并放入到集合中返回/*** @param method 目标方法* @return 所有形参的名称, 并放入到集合中返回*/
public List<String> getParameterNames(Method method) {List<String> parametersList = new ArrayList<String>();//获取到所有的参数名称, 而不是参数类型的名称//这里有一个小细节-->在默认情况下 parameter.getName()//得到的名字不是形参真正的名字, 而是 [arg0, arg1, arg2...]//, 这里我们要引入一个插件, 使用java8的特性, 这样才能解决Parameter[] parameters = method.getParameters();//遍历parameters, 取出名字, 放入parametersListfor (Parameter parameter : parameters) {parametersList.add(parameter.getName());}System.out.println("目标方法的形参参数列表=" + parametersList);return parametersList;
}

3.parameter.getName()得到的名字不是形参真正的名字, 而是 [arg0, arg1, arg2…].
Parameter[] parameters = method.getParameters();
parameters=[javax.servlet.http.HttpServletRequest arg0,
                     javax.servlet.http.HttpServletResponse arg1,
                     java.lang.String arg2]

parameter.getName() ⇒ [arg0, arg1, arg2]

我们需要引入一个插件.
pom.xmlbuild节点内插入以下代码 -parameters, 点击右上角刷新

<build><finalName>zzw-springmvc2(你的文件名)</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><source>1.8</source><target>1.8</target><compilerArgs><arg>-parameters</arg></compilerArgs><encoding>utf-8</encoding></configuration></plugin></plugins>
</build>

3.1点击maven管理, Lifecycle目录下, clean项目, 重启(不是重新部署)tomcat.
在这里插入图片描述

5.测试
在这里插入图片描述在这里插入图片描述

实现任务阶段七

🍍完成简单视图解析

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

●完成任务说明
-用户输入白骨精, 可以登陆成功, 否则失败
-根据登陆的结果, 可以重定向或者请求转发到 login_ok.jsp / login_error.jsp, 并显示妖怪名

-思路分析示意图
在这里插入图片描述

1.在webapp目录下新建

1)login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登陆页面</title>
</head>
<body>
<h1>登陆页面</h1>
<form action="?" method="post">妖怪名: <input type="text" name="mName"/><br/><input type="submit" value="登录">
</form>
</body>
</html>

2)login_ok.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登录成功</title>
</head>
<body>
<h1>登陆成功</h1>
欢迎你: ${?}
</body>
</html>

3)login_error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登录失败</title>
</head>
<body>
<h1>登陆失败</h1>
sorry, 登陆失败 ${?}
</body>
</html>

2.MonsterService增加一个方法login

public interface MonsterService {....//增加方法, 处理登录public boolean login(String name);
}

MonsterServiceImpl实现它

@Service
public class MonsterServiceImpl implements MonsterService {....@Overridepublic boolean login(String name) {//实际上会到DB验证->这里我们模拟一下if ("白骨精".equals(name)) {return true;} else {return false;}}
}

3.MonsterController添加一个方法login

//处理妖怪登陆的方法, 返回要请求转发/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);boolean b = monsterService.login(mName);if (b) {//登陆成功return "forward:/login_ok.jsp";} else {//登陆失败return "forward:/login_error.jsp";}
}

接着 login.jsp填充action="/monster/login"

<h1>登陆页面</h1>
<%--第一个/会被解析 http://localhost:8080
/monster/login => http://localhost:8080/monster/login--%>
<form action="/monster/login" method="post">妖怪名: <input type="text" name="mName"/><br/><input type="submit" value="登录">
</form>

4.测试
在这里插入图片描述在这里插入图片描述

如果输入中文, 发现提交的数据有中文乱码问题, 因为是post请求.
在这里插入图片描述
在这里插入图片描述


解决方案
我们在底层解决乱码问题.
ZzwDispatcherServlet前端控制器完成分发请求任务executeDispatcher()方法内, 添加如下代码 request.setCharacterEncoding("utf-8");即可

//2.返回的Map<String, String[]> String: 表示http请求的参数名
//  String[]: 表示http请求的参数值, 为什么是数组
//处理提交的数据中文乱码问题
request.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = request.getParameterMap();

测试
在这里插入图片描述


5.在ZzwDispatcherServletexecuteDispatcher方法, 添加如下代码

//上面代码省略.../*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参=> 封装到参数数组=> 然后以反射调用的方式传递给目标方法* public Object invoke(Object var1, Object... var2)...*///反射调用目标方法
Object result = zzwHandler.getMethod().invoke(zzwHandler.getController(), params);//这里就是对返回的结果进行解析=>原生springmvc 可以通过视图解析器来完成
//这里老师让我们直接解析, 只要把视图解析器的核心机制表达清楚就OK
//instanceof 判断 运行类型
if (result instanceof String) {String viewName = (String) result;if (viewName.contains(":")) {//说明你返回的String 结果forward:/login_ok.jsp 或者 redirect:/xxx/xx/xx.xxString viewType = viewName.split(":")[0];//forward | redirectString viewPage = viewName.split(":")[1];//表示你要跳转的页面//判断是forward 还是 redirectif ("forward".equals(viewType)) {//说明你希望请求转发request.getRequestDispatcher(viewPage).forward(request, response);} else if ("redirect".equals(viewType)) {//说明你希望重定向//如果是redirect, 那么这里需要拼接Application  Context. 只不过这个项目的Application Context 正好是 /response.sendRedirect(viewPage);}} else {//默认是请求转发request.getRequestDispatcher(viewName).forward(request, response);}
}//这里还可以扩展

6.测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解决{?}:将信息保存到request域, 在页面显示.
6.MonsterController修改login方法, 将mName保存到request域

//处理妖怪登陆的方法, 返回要请求转发/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);//将nName设置到request域request.setAttribute("mName", mName);boolean b = monsterService.login(mName);if (b) {//登陆成功return "forward:/login_ok.jsp";} else {//登陆失败return "forward:/login_error.jsp";}
}

6.1修改login_ok.jsp. 设置isELIgnored="false", 否则el表达式不会生效, 默认是true

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head><title>登录成功</title>
</head>
<body>
<h1>登陆成功</h1>
欢迎你: ${requestScope.mName}
</body>
</html>

6.2修改login_error.jsp.设置isELIgnored="false", 否则el表达式不会生效 默认是true

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head><title>登录失败</title>
</head>
<body>
<h1>登陆失败</h1>
sorry, 登陆失败: ${requestScope.mName}
</body>
</html>

6.3测试
在这里插入图片描述在这里插入图片描述

7.演示redirect.
MonsterController修改login方法

//处理妖怪登陆的方法, 返回要请求转发/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);//将nName设置到request域request.setAttribute("mName", mName);boolean b = monsterService.login(mName);if (b) {//登陆成功//return "forward:/login_ok.jsp";//测试从定向return "redirect:/login_ok.jsp";👈} else {//登陆失败return "forward:/login_error.jsp";}
}

7.1测试

在这里插入图片描述之所有不显示, 是因为请求方式是重定向.
在这里插入图片描述
在这里插入图片描述

7.2演示默认方式(forward)
MonsterController修改login方法

//处理妖怪登陆的方法, 返回要请求转发/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);//将nName设置到request域request.setAttribute("mName", mName);boolean b = monsterService.login(mName);if (b) {//登陆成功//return "forward:/login_ok.jsp";//测试从定向//return "redirect:/login_ok.jsp";//测试默认方式(forward)return "/login_ok.jsp";👈} else {//登陆失败return "forward:/login_error.jsp";}
}

8 将登录页面提交的参数名改成monsterName, 还会不会登陆成功?

<h1>登陆页面</h1>
<%--第一个/会被解析 http://localhost:8080
/monster/login => http://localhost:8080/monster/login--%>
<form action="/monster/login" method="post">妖怪名: <input type="text" name="monsterName"/><br/><input type="submit" value="登录">
</form>

8.1测试
在这里插入图片描述在这里插入图片描述

8.2 解决方案: 在MonsterControllerlogin方法, 形参mName加上@RequestParam(value = "monsterName")

//处理妖怪登陆的方法, 返回要请求转发/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request,HttpServletResponse response,@RequestParam(value = "monsterName") String mName) {System.out.println("---接收到mName---" + mName);//将nName设置到request域request.setAttribute("mName", mName);boolean b = monsterService.login(mName);if (b) {//登陆成功//return "forward:/login_ok.jsp";//测试从定向//return "redirect:/login_ok.jsp";//测试默认方式-forwardreturn "/login_ok.jsp";} else {//登陆失败return "forward:/login_error.jsp";}
}

8.3测试
在这里插入图片描述在这里插入图片描述

实现任务阶段八

🍍完成返回JSON格式数据-@ResponseBody

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

●完成任务说明
-在实际开发中, 比如前后端分离的项目, 通常是直接返回json数据给客户端/浏览器
-客户端/浏览器接收到数据后, 自己决定如何处理和显示
-测试页面 浏览器输入: http://localhost:8080/monster/list/json

🥦分析+代码实现

在这里插入图片描述

1.在com.zzw.zzwspringmvc.annotation下新建@ResponseBody

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {String value() default "";
}

2.MonsterController添加方法listMonsterByJson

@Controller
public class MonsterController {//@Autowired表示要完成属性的装配.@Autowiredprivate MonsterService monsterService;/*** 编写方法, 返回json格式的数据* 老师梳理* 1.目标方法返回的结果是给springmvc底层反射调用的位置使用* 2.我们在springmvc底层反射调用的位置, 接收到结果并解析即可* 3.方法上标注了@ResponseBody 表示希望以json格式返回给客户端/浏览器* 4.目标方法的实参, 在springmvc底层通过封装好的参数数组, 传入...* @param request* @param respons* @return*/@RequestMapping("/monster/list/json")@ResponseBodypublic List<Monster> listMonsterByJson(HttpServletRequest request,HttpServletResponse respons) {List<Monster> monsters = monsterService.listMonster();return monsters;}
}

3.pom.xml引入jackson, 刷新. 注意: 这里我们不使用gson
注意: jackson-databind

<!--引入jackson, 使用它的工具类可以进行json操作-->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.4</version>
</dependency>

3.1com.zzw包下新建一个测试类ZzwTest

public class ZzwTest {public static void main(String[] args) {List<Monster> monsters = new ArrayList<Monster>();monsters.add(new Monster(100, "牛魔王", "芭蕉扇", 400));monsters.add(new Monster(200, "汤姆猫", "抓老鼠", 200));//把monsters 转成jsonObjectMapper objectMapper = new ObjectMapper();try {String monsterJson = objectMapper.writeValueAsString(monsters);System.out.println("monsterJson=" + monsterJson);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}
}

4.ZzwDispatcherServletexecuteDispatcher, 在反射调用目标方法的位置扩展代码

if (result instanceof String) {String viewName = (String) result;......
}//这里还可以扩展
else if (result instanceof ArrayList) {//如果是ArrayList//判断目标方法是否有@ResponseBody注解Method method = zzwHandler.getMethod();if (method.isAnnotationPresent(ResponseBody.class)) {//把result [ArrayList] 转成json格式数据->返回//这里我们需要使用到java中如何将 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();}
}

🥦完成测试

1.浏览器测试
在这里插入图片描述
2.也可以在线转成规整的json格式
JSON工具在线解析
在这里插入图片描述

3.也可以使用Postman进行测试
在这里插入图片描述


🔜 下一篇预告 🔜
敬请期待:SpringMVC系列九: 数据格式化与验证及国际化


📚 目录导航 📚

  1. SpringMVC系列一: 初识SpringMVC
  2. SpringMVC系列二: 请求方式介绍
  3. SpringMVC系列三: Postman(接口测试工具)
  4. SpringMVC系列四: Rest-优雅的url请求风格
  5. SpringMVC系列五: SpringMVC映射请求数据
  6. SpringMVC系列六: 视图和视图解析器
  7. SpringMVC系列七: 手动实现SpringMVC底层机制-上
  8. SpringMVC系列八: 手动实现SpringMVC底层机制-下

💬 读者互动 💬
在学习SpringMVC底层机制的过程中,你有哪些疑问或需要帮助的地方?欢迎在评论区留言,我们一起讨论。


在这里插入图片描述

相关文章:

SpringMVC系列八: 手动实现SpringMVC底层机制-下

手动实现SpringMVC底层机制-下 实现任务阶段五&#x1f34d;完成Spring容器对象的自动装配-Autowired 实现任务阶段六&#x1f34d;完成控制器方法获取参数-RequestParam1.&#x1f966;将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用2.&a…...

【昇思初学入门】第八天打卡-模型保存与加载

模型保存与加载 学习心得 保存 CheckPoint 格式文件&#xff0c;在模型训练过程中&#xff0c;可以添加检查点(CheckPoint)用于保存模型的参数&#xff0c;以便进行推理及再训练使用。如果想继续在不同硬件平台上做推理&#xff0c;可通过网络和CheckPoint格式文件生成对应的…...

喜报!极限科技新获得一项国家发明专利授权:“搜索数据库的正排索引处理方法、装置、介质和设备”

近日&#xff0c;极限数据&#xff08;北京&#xff09;科技有限公司&#xff08;简称&#xff1a;极限科技&#xff09;新获得一项国家发明专利授权&#xff0c;专利名为 “搜索数据库的正排索引处理方法、装置、介质和设备”&#xff0c;专利号&#xff1a;ZL 2024 1 0479400…...

深入探讨:UART与USART在单片机中串口的实际应用与实现技巧

单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种集成了处理器、存储器和输入输出接口的微型计算机。它广泛应用于嵌入式系统中&#xff0c;用于控制各类电子设备。UART和USART是单片机中常见的通信接口&#xff0c;负责串行数据传输。下面我们详细介绍它们在单…...

Windows上PyTorch3D安装踩坑记录

直入正题&#xff0c;打开命令行&#xff0c;直接通过 pip 安装 PyTorch3D : (python11) F:\study\2021-07\python>pip install pytorch3d Looking in indexes: http://mirrors.aliyun.com/pypi/simple/ ERROR: Could not find a version that satisfies the requirement p…...

操作符详解(上) (C语言)

操作符详解&#xff08;上&#xff09; 一. 进制转换1. 二进制2. 二进制的转换 二. 原码 补码 反码三. 操作符的分类四. 结构成员访问操作符1. 结构体的声明2. 结构体成员访问操作符 一. 进制转换 1. 二进制 在学习操作符之前&#xff0c;我们先了解一些2进制、8进制、10进制…...

使用 audit2allow 工具添加SELinux权限的方法

1. audit2allow工具的使用 audit2allow 命令的作用是分析日志&#xff0c;并提供允许的建议规则或拒绝的建议规则。 1.1 audit2allow的安装 sudo apt-get install policycoreutilssudo apt install policycoreutils-python-utils 1.2 auditallow的命令 命令含义用法-v--ve…...

一文弄懂FPGA

一、FPGA简介 什么是FPGA&#xff1f; FPGA&#xff08;Field-Programmable Gate Array&#xff09;是一种可编程逻辑器件&#xff0c;可以在现场通过硬件描述语言&#xff08;HDL&#xff09;进行配置。它具有高度的灵活性和并行处理能力&#xff0c;广泛应用于通信、计算、…...

Rust 中使用 :: 这种语法的几种情况

文章目录 1. 访问模块成员&#xff1a;2. 访问关联函数或静态方法&#xff1a;3. 访问 trait 的关联类型或关联常量4. 指定泛型类型参数 1. 访问模块成员&#xff1a; mod utils {pub fn do_something() { /* ... */ } }let result utils::do_something();2. 访问关联函数或静…...

Ruby langchainrb gem and custom configuration for the model setup

题意&#xff1a;Ruby 的 langchainrb gem 以及针对模型设置的自定义配置 问题背景&#xff1a; I am working in a prototype using the gem langchainrb. I am using the module assistant module to implemente a basic RAG architecture. 我正在使用 langchainrb 这个 ge…...

高校新生如何选择最优手机流量卡?

一年一度的高考已经结束了&#xff0c;愿广大学子金榜题名&#xff0c;家长们都给孩子准备好了手机&#xff0c;那么手机流量卡应该如何选择呢&#xff1f; 高校新生在选择手机流量卡时&#xff0c;需要综合考量流量套餐、费用、网络覆盖、售后服务等多方面因素&#xff0c;以下…...

QT QML 生成二维码

Qt生成二维码 C++版 文章目录 步骤1:安装libqrencode步骤2:创建C++类生成二维码步骤3:将C++类与QML绑定步骤4:创建QML界面步骤5:配置项目文件总结在Qt QML中实现二维码生成,可以使用一个C++库来生成二维码,然后将生成的二维码图像传递给QML进行显示。一个常用的二维码生…...

IDEA中Maven--下载安装自己适配的版本---理解

Maven解释&#xff1a; Maven是一个强大的项目管理工具和构建工具&#xff0c;主要用于Java项目。它能够帮助开发团队管理项目的依赖、构建项目、发布文档和报告&#xff0c;并能够自动化许多重复的任务。 Maven的主要作用包括&#xff1a; 依赖管理&#xff1a;Maven能够管理…...

【osgEarth】Ubuntu 22.04 源码编译osgEarth 3.5

下载源代码 git clone --depth1 https://dgithub.xyz/gwaldron/osgearth -b osgearth-3.5 下载子模块 git submodule update --init 如果下载不过来&#xff0c;就手动修改下.git/config文件&#xff0c;将子模块的地址替换成加速地址 (base) yeqiangyeqiang-Default-string…...

ASP.NET Core 6.0 使用 资源过滤器和行为过滤器

1.AOP 面向切面编程 概念 AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过预定义的模式(即“切面”)对程序的横切关注点进行模块化。横切关注点是一个在多个应用模块中出现的概念,例如日志记录、事务管理、安全检查等。AOP允许开发者定义“切面”…...

电脑屏幕花屏怎么办?5个方法解决问题!

“我刚刚打开电脑就发现我的电脑屏幕出现了花屏的情况。这让我很困惑&#xff0c;我应该怎么解决这个问题呢&#xff1f;求帮助。” 在这个数字时代的浪潮中&#xff0c;电脑早已成为我们生活中不可或缺的一部分。然而&#xff0c;当你正沉浸在紧张的游戏对战中&#xff0c;或是…...

git 初基本使用-----------笔记

Git命令 下载git 打开Git官网&#xff08;git-scm.com&#xff09;&#xff0c;根据自己电脑的操作系统选择相应的Git版本&#xff0c;点击“Download”。 基本的git命令使用 可以在项目文件下右击“Git Bash Here” &#xff0c;也可以命令终端下cd到指定目录执行初始化命令…...

Redis-数据类型-Bit的基本操作-getbit-setbit-Bitmap

文章目录 0、Bitmaps&#xff08;位图&#xff09;1、查看redis是否启动2、通过客户端连接redis3、切换到db7数据库4、设置&#xff08;或覆盖&#xff09;一个键&#xff08;key&#xff09;的值&#xff08;value&#xff09;5、获取存储在给定键&#xff08;key&#xff09;…...

统信UOS上鼠标右键菜单中添加自定义内容

原文链接&#xff1a;统信UOS上鼠标右键菜单中添加自定义内容 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在统信UOS桌面操作系统上鼠标右键菜单中添加自定义内容的文章。通过自定义鼠标右键菜单&#xff0c;可以大大提升日常操作的效率和便捷性。本文将详细…...

学习入门 chatgpt原理 一

学习文章&#xff1a;人人都能看懂的chatGpt原理课 笔记作为学习用&#xff0c;侵删 Chatph和自然语言处理 什么是ChatGpt ChatGPT&#xff08;Chat Generative Pre-training Transformer&#xff09; 是一个 AI 模型&#xff0c;属于自然语言处理&#xff08; Natural Lang…...

生命在于学习——Python人工智能原理(4.7)

四、Python的程序结构与函数 4.4 函数 函数能将代码划分为若干模块&#xff0c;每一个模块可以相对独立的实现某一个功能&#xff0c;函数有两个主要功能&#xff0c;分别是降低编程难度和实现代码复用&#xff0c;函数是一种功能抽象&#xff0c;复用它可以将一个复杂的大问…...

经典游戏案例:仿植物大战僵尸

学习目标&#xff1a;仿植物大战僵尸核心玩法实现 游戏画面 项目结构目录 部分核心代码 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Random UnityEngine.Random;public enum…...

[Day 18] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

強化學習與生成對抗網絡(GAN) 引言 強化學習 (Reinforcement Learning, RL) 和生成對抗網絡 (Generative Adversarial Networks, GANs) 是現代人工智能中的兩大關鍵技術。強化學習使得智能體可以通過與環境交互學習最佳行動策略&#xff0c;而生成對抗網絡則通過兩個相互競爭…...

【Mac】DMG Canvas for mac(DMG镜像制作工具)软件介绍

软件介绍 DMG Canvas 是一款专门用于创建 macOS 磁盘映像文件&#xff08;DMG&#xff09;的软件。它的主要功能是让用户可以轻松地设计、定制和生成 macOS 上的安装器和磁盘映像文件&#xff0c;以下是它的一些主要特点和功能。 主要特点和功能 1. 用户界面设计 DMG Canva…...

RAG分块方法 从固定大小到自然语言处理分块——深入研究文本分块技术

发掘文本分块-准确的搜索结果和更智能的语言模型背后的秘诀&#xff0c;通过了解如何有效地分块文本&#xff0c;我们可以改进索引文档、处理用户查询和利用搜索结果的方式。准备好揭开文本分块的秘密了吗? 一、了解分块 分块是一种旨在嵌入尽可能少噪音的内容&#xff0c;同…...

FFmpeg 系列

&#x1f4da; 此篇文章是先引入ffmpeg的概念以及主要的功能&#xff0c;后面会根据每一个特点进行详解&#xff0c;喜欢ffmpeg的可以持续关注。 ffmpeg是什么&#xff1f; FFmpeg 是一个开源的跨平台音视频处理工具&#xff0c;它可以用来录制、转换以及流化音视频内容。具体…...

240626_昇思学习打卡-Day8-稀疏矩阵

240626_昇思学习打卡-Day8-稀疏矩阵 稀疏矩阵 在一些应用场景中&#xff0c;比如训练二值化图像分割时&#xff0c;图像的特征是稀疏的&#xff0c;使用一堆0和极个别的1表示这些特征即费事又难看&#xff0c;此时就可以使用稀疏矩阵。通过参考大佬博文&#xff0c;结合个人理…...

Docker: 使用容器化数据库

使用容器化数据库 使用本地容器化数据库提供了灵活性和简易的设置,使您能够在不需要传统数据库安装开销的情况下,紧密模拟生产环境。Docker 简化了这一过程,只需几条命令就可以在隔离的容器中部署、管理和扩展数据库。 在本指南中,您将学习如何: 运行本地容器化数据库访…...

Oracle对用户敏感数据进行编码处理

由于系统运行时间比较长&#xff0c;没有对用户的身份证号、邮箱、手机号进行脱敏处理&#xff0c;后期对数据进行了编码。 更新表数据 sql UPDATE sys_staff SET MOBIL_PHONE CASEWHEN MOBIL_PHONE IS NULL THEN ELSE utl_raw.cast_to_varchar2(utl_encode.base64_encode(ut…...

VXLAN详解:概念、架构、原理、搭建过程、常用命令与实战案例

一、VXLAN概述 1.1 VXLAN的定义 VXLAN&#xff08;Virtual Extensible LAN&#xff0c;虚拟可扩展局域网&#xff09;是一种网络虚拟化技术&#xff0c;通过在现有IP网络上创建虚拟网络&#xff0c;使数据中心可以实现大规模的网络隔离和扩展。VXLAN使用MAC-in-UDP封装技术&a…...