网站做虚假广告,行政审批局政务服务网站建设情况,网站 图片延时加载,电商网站开发用什么软件好手动实现SpringMVC底层机制-下 实现任务阶段五#x1f34d;完成Spring容器对象的自动装配-Autowired 实现任务阶段六#x1f34d;完成控制器方法获取参数-RequestParam1.#x1f966;将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用2.完成Spring容器对象的自动装配-Autowired 实现任务阶段六完成控制器方法获取参数-RequestParam1.将 方法的 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.EntryString, 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.java的init()方法的最后添加三行代码
//编写方法, 完成自己的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.修改MonsterController的listMonster方法
RequestMapping(value /monster/list)
public void listMonster(HttpServletRequest request, HttpServletResponse response) {//设置返回编码和返回类型response.setContentType(text/html;charsetutf-8);StringBuilder content new StringBuilder(h1妖怪列表信息/h1);//单线程使用StringBuildercontent.append(table border1px width500px styleborder-collapse:collapse);//调用monsterServiceListMonster monsters monsterService.listMonster();for (Monster monster : monsters) {content.append(trtd monster.getId() /tdtd monster.getName() /tdtd monster.getAge() /tdtd 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 参数封装到数组, 进行反射调用
修改ZzwDispatcherServlet的executeDispatcher()方法
//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler getZzwHandler(request);if (zzwHandler null) {//说明用户请求的路径/资源不存在response.getWriter().print(h1404 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 ListMonster findMonsterByName(String name);
}MonsterServiceImpl将其实现
Service
public class MonsterServiceImpl implements MonsterService {public ListMonster findMonsterByName(String name) {//这里我们模拟数据-DBListMonster monsters new ArrayListMonster();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集合ListMonster findMonsters new ArrayListMonster();//遍历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(h1404 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.返回的MapString, String[] String: 表示http请求的参数名// String[]: 表示http请求的参数值, 想一下为什么是数组?//http://localhost:8080/monster/find?name牛魔王hobby打篮球hobby喝酒hobby吃肉(防止有类似checkbox)MapString, String[] parameterMap request.getParameterMap();//3.遍历parameterMap, 将请求参数, 按照顺序填充到实参数组for (Map.EntryString, 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;charsetutf8);System.out.println(---接收到的name--- name);StringBuilder content new StringBuilder(h1妖怪列表信息/h1);//单线程使用StringBuildercontent.append(table border1px width500px styleborder-collapse:collapse);//调用monsterServiceListMonster monsters monsterService.findMonsterByName(name);for (Monster monster : monsters) {content.append(trtd monster.getId() /tdtd monster.getName() /tdtd monster.getAge() /tdtd 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.去掉MonsterController 的findMonsterByName()方法的name字段的RequestParam注解
//增加方法, 通过name返回对应的monster对象
RequestMapping(value /monster/find)
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,String name) {.....
}2.ZzwDispatcherServlet的executeDispatcher()的else分支内增加如下代码, 并增加方法getParameterNames
//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler getZzwHandler(request);if (zzwHandler null) {//说明用户请求的路径/资源不存在response.getWriter().print(h1404 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.返回的MapString, String[] String: 表示http请求的参数名// String[]: 表示http请求的参数值, 为什么是数组//MapString, String[] parameterMap request.getParameterMap();//2.遍历parameterMap, 将请求参数, 按照顺序填充到实参数组for (Map.EntryString, 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.对得到的目标方法的所有形参名进行遍历, 如果匹配就把当前请求的参数值, 填充到paramsListString 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 ListString getParameterNames(Method method) {ListString parametersList new ArrayListString();//获取到所有的参数名称, 而不是参数类型的名称//这里有一个小细节--在默认情况下 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.xml的build节点内插入以下代码 -parameters, 点击右上角刷新
buildfinalNamezzw-springmvc2(你的文件名)/finalNamepluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion3.7.0/versionconfigurationsource1.8/sourcetarget1.8/targetcompilerArgsarg-parameters/arg/compilerArgsencodingutf-8/encoding/configuration/plugin/plugins
/build3.1点击maven管理, Lifecycle目录下, clean项目, 重启(不是重新部署)tomcat.
5.测试
实现任务阶段七
完成简单视图解析
功能说明: 通过方法返回的String, 转发或者重定向到指定页面
●完成任务说明 -用户输入白骨精, 可以登陆成功, 否则失败 -根据登陆的结果, 可以重定向或者请求转发到 login_ok.jsp / login_error.jsp, 并显示妖怪名
-思路分析示意图
1.在webapp目录下新建
1)login.jsp
% page contentTypetext/html;charsetUTF-8 languagejava %
html
headtitle登陆页面/title
/head
body
h1登陆页面/h1
form action? methodpost妖怪名: input typetext namemName/br/input typesubmit value登录
/form
/body
/html2)login_ok.jsp
% page contentTypetext/html;charsetUTF-8 languagejava %
html
headtitle登录成功/title
/head
body
h1登陆成功/h1
欢迎你: ${?}
/body
/html3)login_error.jsp
% page contentTypetext/html;charsetUTF-8 languagejava %
html
headtitle登录失败/title
/head
body
h1登陆失败/h1
sorry, 登陆失败 ${?}
/body
/html2.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 methodpost妖怪名: input typetext namemName/br/input typesubmit value登录
/form4.测试
如果输入中文, 发现提交的数据有中文乱码问题, 因为是post请求. 解决方案 我们在底层解决乱码问题. 在ZzwDispatcherServlet前端控制器的完成分发请求任务的executeDispatcher()方法内, 添加如下代码 request.setCharacterEncoding(utf-8);即可
//2.返回的MapString, String[] String: 表示http请求的参数名
// String[]: 表示http请求的参数值, 为什么是数组
//处理提交的数据中文乱码问题
request.setCharacterEncoding(utf-8);
MapString, String[] parameterMap request.getParameterMap();测试 5.在ZzwDispatcherServlet的executeDispatcher方法, 添加如下代码
//上面代码省略.../*** 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. 设置isELIgnoredfalse, 否则el表达式不会生效, 默认是true
% page contentTypetext/html;charsetUTF-8 languagejava isELIgnoredfalse %
html
headtitle登录成功/title
/head
body
h1登陆成功/h1
欢迎你: ${requestScope.mName}
/body
/html6.2修改login_error.jsp.设置isELIgnoredfalse, 否则el表达式不会生效 默认是true
% page contentTypetext/html;charsetUTF-8 languagejava isELIgnoredfalse %
html
headtitle登录失败/title
/head
body
h1登陆失败/h1
sorry, 登陆失败: ${requestScope.mName}
/body
/html6.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 methodpost妖怪名: input typetext namemonsterName/br/input typesubmit value登录
/form8.1测试
8.2 解决方案: 在MonsterController的login方法, 形参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 ListMonster listMonsterByJson(HttpServletRequest request,HttpServletResponse respons) {ListMonster monsters monsterService.listMonster();return monsters;}
}3.pom.xml引入jackson, 刷新. 注意: 这里我们不使用gson 注意: jackson-databind
!--引入jackson, 使用它的工具类可以进行json操作--
dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.4/version
/dependency3.1com.zzw包下新建一个测试类ZzwTest
public class ZzwTest {public static void main(String[] args) {ListMonster monsters new ArrayListMonster();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.ZzwDispatcherServlet的executeDispatcher, 在反射调用目标方法的位置扩展代码
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;charsetutf-8);PrintWriter writer response.getWriter();writer.write(resultJson);writer.flush();writer.close();}
}完成测试
1.浏览器测试 2.也可以在线转成规整的json格式 JSON工具在线解析
3.也可以使用Postman进行测试 下一篇预告 敬请期待SpringMVC系列九: 数据格式化与验证及国际化 目录导航
SpringMVC系列一: 初识SpringMVCSpringMVC系列二: 请求方式介绍SpringMVC系列三: Postman(接口测试工具)SpringMVC系列四: Rest-优雅的url请求风格SpringMVC系列五: SpringMVC映射请求数据SpringMVC系列六: 视图和视图解析器SpringMVC系列七: 手动实现SpringMVC底层机制-上SpringMVC系列八: 手动实现SpringMVC底层机制-下 … 读者互动 在学习SpringMVC底层机制的过程中你有哪些疑问或需要帮助的地方欢迎在评论区留言我们一起讨论。