一個網(wǎng)站交互怎么做整合營銷理論
手動實現(xiàn)SpringMVC底層機制-下
- 實現(xiàn)任務(wù)階段五
- 🍍完成Spring容器對象的自動裝配-@Autowired
- 實現(xiàn)任務(wù)階段六
- 🍍完成控制器方法獲取參數(shù)-@RequestParam
- 1.🥦將 方法的 HttpServletRequest 和 HttpServletResponse 參數(shù)封裝到數(shù)組, 進(jìn)行反射調(diào)用
- 2.🥦在方法形參處, 指定 @RequestParam, 將對應(yīng)的實參封裝到參數(shù)數(shù)組, 進(jìn)行反射調(diào)用
- 3.🥦在方法形參 沒有指定 @RequestParam, 按照默認(rèn)參數(shù)名獲取值, 進(jìn)行反射調(diào)用
- 實現(xiàn)任務(wù)階段七
- 🍍完成簡單視圖解析
- 實現(xiàn)任務(wù)階段八
- 🍍完成返回JSON格式數(shù)據(jù)-@ResponseBody
- 🥦分析+代碼實現(xiàn)
- 🥦完成測試
在本篇文章中,我們將繼續(xù)深入探討如何手動實現(xiàn)SpringMVC的底層機制。通過這兩部分的學(xué)習(xí),你將全面理解SpringMVC的工作原理。
?? 上一講: SpringMVC系列七: 手動實現(xiàn)SpringMVC底層機制-上
🔧 需要用到的項目: zzw-springmvc項目
實現(xiàn)任務(wù)階段五
🍍完成Spring容器對象的自動裝配-@Autowired
說明: 完成Spring容器中對象的注入/自動裝配
示意圖[分析說明]
分析:
加入@Autowired
注解, 進(jìn)行對象屬性的裝配. -如圖
測試:
瀏覽器輸入 http://localhost:8080/monster/list
, 返回列表信息
代碼實現(xiàn):
1.在com.zzw.zzwspringmvc.annotation
下新建@Autowired
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {String value() default "";
}
1.1MonsterController
添加屬性monsterService
, 標(biāo)注@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) {//判斷當(dāng)前這個字段, 是否有@Autowiredif (declaredField.isAnnotationPresent(Autowired.class)) {//有@Autowired//得到當(dāng)前這個字段的@AutowiredAutowired autowiredAnnotation = declaredField.getDeclaredAnnotation(Autowired.class);String beanName = autowiredAnnotation.value();if ("".equals(beanName)) {//如果沒有設(shè)置value, 按照默認(rèn)規(guī)則//即得到字段類型名稱的首字母小寫, 作為名字來進(jìn)行裝配Class<?> type = declaredField.getType();beanName = type.getSimpleName().substring(0, 1).toLowerCase()+ type.getSimpleName().substring(1);}//如果設(shè)置了value, 直接按照beanName來裝配//從ioc容器中獲取beanif (ioc.get(beanName) == null) {//說明你指定的名字對應(yīng)的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) {//設(shè)置返回編碼和返回類型response.setContentType("text/html;charset=utf-8");StringBuilder content = new StringBuilder("<h1>妖怪列表信息</h1>");//單線程使用StringBuildercontent.append("<table border='1px' width='500px' style='border-collapse:collapse'>");//調(diào)用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
實現(xiàn)任務(wù)階段六
🍍完成控制器方法獲取參數(shù)-@RequestParam
功能說明: 自定義@RequestParam 和 方法參數(shù)名獲取參數(shù)
完成: 將 方法的 HttpServletRequest 和 HttpServletResponse 參數(shù)封裝到數(shù)組, 進(jìn)行反射調(diào)用
完成: 在方法參數(shù) 指定 @RequestParam 的參數(shù)封裝到參數(shù)數(shù)組, 進(jìn)行反射調(diào)用
完成: 在方法參數(shù) 沒有指定 @RequestParam, 按照默認(rèn)參數(shù)名獲取值, 進(jìn)行反射調(diào)用
示意圖[分析說明]
1.🥦將 方法的 HttpServletRequest 和 HttpServletResponse 參數(shù)封裝到數(shù)組, 進(jìn)行反射調(diào)用
修改ZzwDispatcherServlet
的executeDispatcher()
方法
//編寫方法, 完成分發(fā)請求任務(wù)
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//說明用戶請求的路徑/資源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射調(diào)用控制器的方法//目標(biāo)將: HttpServletRequest 和 HttpServletResponse 封裝到參數(shù)數(shù)組//1. 得到目標(biāo)方法的所有形參參數(shù)信息[返回對應(yīng)的數(shù)組]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 創(chuàng)建一個參數(shù)數(shù)組[對應(yīng)實參數(shù)組], 在后面反射調(diào)用目標(biāo)方法時, 會使用到Object[] params = new Object[parameterTypes.length];//3.遍歷parameterTypes形參數(shù)組, 根據(jù)形參數(shù)組信息, 將實參填充到實參數(shù)組中for (int i = 0; i < parameterTypes.length; i++) {//取出每一個形參"類型"Class<?> parameterType = parameterTypes[i];//如果這個形參類型是HttpServletRequest, 將request填充到params//在原生的SpringMVC中, 是按照類型來進(jìn)行匹配的, 老師這里簡化, 使用名字來匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if("HttpServletResponse".equals(parameterType.getSimpleName())){params[i] = response;}}/*** 1.下面這樣寫, 其實是針對目標(biāo)方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()* .invoke(zzwHandler.getController(), request, response)* 2.這里我們準(zhǔn)備將需要傳遞給目標(biāo)方法的 實參 => 封裝到參數(shù)數(shù)組* => 然后以反射調(diào)用的方式傳遞給目標(biāo)方法* 源碼: public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}
2.🥦在方法形參處, 指定 @RequestParam, 將對應(yīng)的實參封裝到參數(shù)數(shù)組, 進(jìn)行反射調(diào)用
1.MonsterService
新添方法findMonsterByName
public interface MonsterService {//增加方法, 通過傳入的name, 返回對應(yīng)的monster列表public List<Monster> findMonsterByName(String name);
}
MonsterServiceImpl
將其實現(xiàn)
@Service
public class MonsterServiceImpl implements MonsterService {public List<Monster> findMonsterByName(String name) {//這里我們模擬數(shù)據(jù)->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));//創(chuàng)建集合并且返回查詢到的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 注解 標(biāo)注在目標(biāo)方法的參數(shù)上, 表示對應(yīng)http請求的參數(shù)*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)//runtime表示在反射時可以拿到這個注解
@Documented
public @interface RequestParam {String value() default "";
}
3.ZzwDispatcherServlet
增添代碼
注意點
1)method.getParameters():
得到所有的形參
2)method.getParameterTypes():
得到形參的類型
//編寫方法, 完成分發(fā)請求任務(wù)
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//說明用戶請求的路徑/資源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射調(diào)用控制器的方法//目標(biāo)將: HttpServletRequest 和 HttpServletResponse 封裝到參數(shù)數(shù)組//1. 得到目標(biāo)方法的所有形參參數(shù)信息[對應(yīng)的數(shù)組]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 創(chuàng)建一個參數(shù)數(shù)組[對應(yīng)實參數(shù)組], 在后面反射調(diào)用目標(biāo)方法時, 會使用到Object[] params = new Object[parameterTypes.length];//3.遍歷parameterTypes形參數(shù)組, 根據(jù)形參數(shù)組信息, 將實參填充到實參數(shù)組中for (int i = 0; i < parameterTypes.length; i++) {//取出每一個形參類型Class<?> parameterType = parameterTypes[i];//如果這個形參是HttpServletRequest, 將request填充到params//在原生的SpringMVC中, 是按照類型來進(jìn)行匹配的, 老師這里簡化, 使用名字來匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {params[i] = response;}}//將http請求參數(shù)封裝到params數(shù)組中, 老韓提示, 要注意填充實參的時候, 順序問題 👈//1.獲取http請求的參數(shù)集合//老韓解讀//2.返回的Map<String, String[]> String: 表示http請求的參數(shù)名// String[]: 表示http請求的參數(shù)值, 想一下為什么是數(shù)組?//http://localhost:8080/monster/find?name=牛魔王&hobby=打籃球&hobby=喝酒&hobby=吃肉(防止有類似checkbox)Map<String, String[]> parameterMap =request.getParameterMap();//3.遍歷parameterMap, 將請求參數(shù), 按照順序填充到實參數(shù)組for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {//取出key. 這個name就是對應(yīng)請求的參數(shù)名String name = entry.getKey();//說明: 只考慮提交的數(shù)據(jù)是單值的情況, 即不考慮類似checkbox提交的數(shù)據(jù)// 老師這里做了簡化, 如果考慮多值情況, 也不難String value = entry.getValue()[0];//我們得到請求的參數(shù)對應(yīng)我們目標(biāo)方法的第幾個形參, 然后將其填充//這里專門編寫一個方法, 得到請求參數(shù)對應(yīng)的是第幾個形參//1. API 2.java內(nèi)力真正增加 3.老韓忠告..int indexRequestParameterIndx =getIndexRequestParameterIndex(zzwHandler.getMethod(), name);if (indexRequestParameterIndx != -1) {//找到對應(yīng)的位置params[indexRequestParameterIndx] = value;} else {//說明并沒有找到@RequestParam注解對應(yīng)的參數(shù), 就會使用默認(rèn)的機制進(jìn)行匹配[待...]//一會再寫}} 👈/*** 1.下面這樣寫, 其實是針對目標(biāo)方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()* .invoke(zzwHandler.getController(), request, response)* 2.這里我們準(zhǔn)備將需要傳遞給目標(biāo)方法的 實參=> 封裝到參數(shù)數(shù)組=> 然后以反射調(diào)用的方式傳遞給目標(biāo)方法* public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}//編寫方法, 返回請求參數(shù)是目標(biāo)方法的第幾個形參/*** @param method 目標(biāo)方法* @param name 請求的參數(shù)名* @return 是目標(biāo)方法的第幾個形參*/
public int getIndexRequestParameterIndex(Method method, String name) {//1.得到method的所有形參參數(shù)Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {//取出當(dāng)前的參數(shù)Parameter parameter = parameters[i];//判斷parameter是不是有@RequestParam注解boolean annotationPresent = parameter.isAnnotationPresent(RequestParam.class);if (annotationPresent) {//說明有@RequestParam//取出當(dāng)前這個參數(shù)的@RequestParam(value = "xxx")RequestParam requestParamAnnotation =parameter.getAnnotation(RequestParam.class);String value = requestParamAnnotation.value();//這里就是匹配的比較if (name.equals(value)) {return i;//找到請求的參數(shù), 對應(yīng)的目標(biāo)方法的形參的位置}}}//如果沒有匹配成功, 就返回-1return -1;
}
4.MonsterController
增加如下方法
//增加方法, 通過name返回對應(yīng)的monster對象
@RequestMapping(value = "/monster/find")
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,@RequestParam(value = "name") String name) {//設(shè)置返回編碼和返回類型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'>");//調(diào)用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, 按照默認(rèn)參數(shù)名獲取值, 進(jìn)行反射調(diào)用
1.去掉MonsterController
的findMonsterByName()
方法的name
字段的@RequestParam注解
//增加方法, 通過name返回對應(yīng)的monster對象
@RequestMapping(value = "/monster/find")
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,String name) {.....
}
2.ZzwDispatcherServlet
的executeDispatcher()
的else分支
內(nèi)增加如下代碼, 并增加方法getParameterNames
//編寫方法, 完成分發(fā)請求任務(wù)
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//說明用戶請求的路徑/資源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射調(diào)用控制器的方法//目標(biāo)將: HttpServletRequest 和 HttpServletResponse 封裝到參數(shù)數(shù)組//1. 得到目標(biāo)方法的所有形參參數(shù)信息[對應(yīng)的數(shù)組]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 創(chuàng)建一個參數(shù)數(shù)組[對應(yīng)實參數(shù)組], 在后面反射調(diào)用目標(biāo)方法時, 會使用到Object[] params = new Object[parameterTypes.length];//3.遍歷parameterTypes形參數(shù)組, 根據(jù)形參數(shù)組信息, 將實參填充到實參數(shù)組中for (int i = 0; i < parameterTypes.length; i++) {//取出每一個形參類型Class<?> parameterType = parameterTypes[i];//如果這個形參是HttpServletRequest, 將request填充到params//在原生的SpringMVC中, 是按照類型來進(jìn)行匹配的, 老師這里簡化, 使用名字來匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {params[i] = response;}}//將http請求參數(shù)封裝到params數(shù)組中, 老韓提示, 要注意填充實參的時候, 順序問題//1.獲取http請求的參數(shù)集合//老韓解讀//http://localhost:8080/monster/find?name=牛魔王&hobby=打籃球&hobby=喝酒&hobby=吃肉(防止有類似checkbox)//2.返回的Map<String, String[]> String: 表示http請求的參數(shù)名// String[]: 表示http請求的參數(shù)值, 為什么是數(shù)組//Map<String, String[]> parameterMap = request.getParameterMap();//2.遍歷parameterMap, 將請求參數(shù), 按照順序填充到實參數(shù)組for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {//取出key. 這個name就是對應(yīng)請求的參數(shù)名String name = entry.getKey();//說明: 只考慮提交的數(shù)據(jù)是單值的情況, 即不考慮類似checkbox提交的數(shù)據(jù)// 老師這里做了簡化, 如果考慮多值情況, 也不難String value = entry.getValue()[0];//我們得到請求的參數(shù)對應(yīng)我們目標(biāo)方法的第幾個形參, 然后將其填充//這里專門編寫一個方法, 得到請求參數(shù)對應(yīng)的是第幾個形參//1. API 2.java內(nèi)力真正增加 3.老韓忠告..int indexRequestParameterIndx =getIndexRequestParameterIndex(zzwHandler.getMethod(), name);if (indexRequestParameterIndx != -1) {//找到對應(yīng)的位置params[indexRequestParameterIndx] = value;
👉👉👉👉👉👉👉} else {//說明并沒有找到@RequestParam注解對應(yīng)的參數(shù), 就會使用默認(rèn)的機制進(jìn)行配置[待...]//思路 //1.得到目標(biāo)方法的所有形參的名稱, 而不是形參類型的名稱-專門編寫一個方法獲取形參名//2.對得到的目標(biāo)方法的所有形參名進(jìn)行遍歷, 如果匹配就把當(dāng)前請求的參數(shù)值, 填充到paramsList<String> parameterNames = getParameterNames(zzwHandler.getMethod());for (int i = 0; i < parameterNames.size(); i++) {//如果請求參數(shù)名和目標(biāo)方法的形參名一樣, 說明匹配成功if (name.equals(parameterNames.get(i))) {params[i] = value;//填充到實參數(shù)組break;}} }}/*** 1.下面這樣寫, 其實是針對目標(biāo)方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()* .invoke(zzwHandler.getController(), request, response)* 2.這里我們準(zhǔn)備將需要傳遞給目標(biāo)方法的 實參=> 封裝到參數(shù)數(shù)組=> 然后以反射調(diào)用的方式傳遞給目標(biāo)方法* public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}//編寫方法, 得到目標(biāo)方法的所有形參的名稱, 并放入到集合中返回/*** @param method 目標(biāo)方法* @return 所有形參的名稱, 并放入到集合中返回*/
public List<String> getParameterNames(Method method) {List<String> parametersList = new ArrayList<String>();//獲取到所有的參數(shù)名稱, 而不是參數(shù)類型的名稱//這里有一個小細(xì)節(jié)-->在默認(rèn)情況下 parameter.getName()//得到的名字不是形參真正的名字, 而是 [arg0, arg1, arg2...]//, 這里我們要引入一個插件, 使用java8的特性, 這樣才能解決Parameter[] parameters = method.getParameters();//遍歷parameters, 取出名字, 放入parametersListfor (Parameter parameter : parameters) {parametersList.add(parameter.getName());}System.out.println("目標(biāo)方法的形參參數(shù)列表=" + 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節(jié)點
內(nèi)插入以下代碼 -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.測試
實現(xiàn)任務(wù)階段七
🍍完成簡單視圖解析
功能說明: 通過方法返回的String, 轉(zhuǎn)發(fā)
或者重定向
到指定頁面
●完成任務(wù)說明
-用戶輸入白骨精, 可以登陸成功, 否則失敗
-根據(jù)登陸的結(jié)果, 可以重定向或者請求轉(zhuǎn)發(fā)到 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
實現(xiàn)它
@Service
public class MonsterServiceImpl implements MonsterService {....@Overridepublic boolean login(String name) {//實際上會到DB驗證->這里我們模擬一下if ("白骨精".equals(name)) {return true;} else {return false;}}
}
3.MonsterController
添加一個方法login
//處理妖怪登陸的方法, 返回要請求轉(zhuǎn)發(fā)/重定向的字符串
@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.測試
如果輸入中文, 發(fā)現(xiàn)提交的數(shù)據(jù)有中文亂碼問題, 因為是post
請求.
解決方案
我們在底層解決亂碼問題.
在ZzwDispatcherServlet前端控制器
的完成分發(fā)請求任務(wù)
的executeDispatcher()方法內(nèi)
, 添加如下代碼 request.setCharacterEncoding("utf-8");
即可
//2.返回的Map<String, String[]> String: 表示http請求的參數(shù)名
// String[]: 表示http請求的參數(shù)值, 為什么是數(shù)組
//處理提交的數(shù)據(jù)中文亂碼問題
request.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = request.getParameterMap();
測試
5.在ZzwDispatcherServlet
的executeDispatcher
方法, 添加如下代碼
//上面代碼省略.../*** 1.下面這樣寫, 其實是針對目標(biāo)方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()* .invoke(zzwHandler.getController(), request, response)* 2.這里我們準(zhǔn)備將需要傳遞給目標(biāo)方法的 實參=> 封裝到參數(shù)數(shù)組=> 然后以反射調(diào)用的方式傳遞給目標(biāo)方法* public Object invoke(Object var1, Object... var2)...*///反射調(diào)用目標(biāo)方法
Object result = zzwHandler.getMethod().invoke(zzwHandler.getController(), params);//這里就是對返回的結(jié)果進(jìn)行解析=>原生springmvc 可以通過視圖解析器來完成
//這里老師讓我們直接解析, 只要把視圖解析器的核心機制表達(dá)清楚就OK
//instanceof 判斷 運行類型
if (result instanceof String) {String viewName = (String) result;if (viewName.contains(":")) {//說明你返回的String 結(jié)果forward:/login_ok.jsp 或者 redirect:/xxx/xx/xx.xxString viewType = viewName.split(":")[0];//forward | redirectString viewPage = viewName.split(":")[1];//表示你要跳轉(zhuǎn)的頁面//判斷是forward 還是 redirectif ("forward".equals(viewType)) {//說明你希望請求轉(zhuǎn)發(fā)request.getRequestDispatcher(viewPage).forward(request, response);} else if ("redirect".equals(viewType)) {//說明你希望重定向//如果是redirect, 那么這里需要拼接Application Context. 只不過這個項目的Application Context 正好是 /response.sendRedirect(viewPage);}} else {//默認(rèn)是請求轉(zhuǎn)發(fā)request.getRequestDispatcher(viewName).forward(request, response);}
}//這里還可以擴展
6.測試
解決{?}:
將信息保存到request域, 在頁面顯示.
6.MonsterController
修改login
方法, 將mName
保存到request域
//處理妖怪登陸的方法, 返回要請求轉(zhuǎn)發(fā)/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);//將nName設(shè)置到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
. 設(shè)置isELIgnored="false"
, 否則el表達(dá)式不會生效, 默認(rèn)是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
.設(shè)置isELIgnored="false"
, 否則el表達(dá)式不會生效 默認(rèn)是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
方法
//處理妖怪登陸的方法, 返回要請求轉(zhuǎn)發(fā)/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);//將nName設(shè)置到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演示默認(rèn)方式(forward)
MonsterController
修改login
方法
//處理妖怪登陸的方法, 返回要請求轉(zhuǎn)發(fā)/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request, HttpServletResponse response, String mName) {System.out.println("---接收到mName---" + mName);//將nName設(shè)置到request域request.setAttribute("mName", mName);boolean b = monsterService.login(mName);if (b) {//登陸成功//return "forward:/login_ok.jsp";//測試從定向//return "redirect:/login_ok.jsp";//測試默認(rèn)方式(forward)return "/login_ok.jsp";👈} else {//登陸失敗return "forward:/login_error.jsp";}
}
8 將登錄頁面提交的參數(shù)名改成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 解決方案: 在MonsterController
的login
方法, 形參mName
加上@RequestParam(value = "monsterName")
//處理妖怪登陸的方法, 返回要請求轉(zhuǎn)發(fā)/重定向的字符串
@RequestMapping("/monster/login")
public String login(HttpServletRequest request,HttpServletResponse response,@RequestParam(value = "monsterName") String mName) {System.out.println("---接收到mName---" + mName);//將nName設(shè)置到request域request.setAttribute("mName", mName);boolean b = monsterService.login(mName);if (b) {//登陸成功//return "forward:/login_ok.jsp";//測試從定向//return "redirect:/login_ok.jsp";//測試默認(rèn)方式-forwardreturn "/login_ok.jsp";} else {//登陸失敗return "forward:/login_error.jsp";}
}
8.3測試
實現(xiàn)任務(wù)階段八
🍍完成返回JSON格式數(shù)據(jù)-@ResponseBody
功能說明: 通過自定義@ResponseBody
, 返回JSON格式數(shù)據(jù)
●完成任務(wù)說明
-在實際開發(fā)中, 比如前后端分離的項目, 通常是直接返回json
數(shù)據(jù)給客戶端/瀏覽器
-客戶端/瀏覽器接收到數(shù)據(jù)后, 自己決定如何處理和顯示
-測試頁面 瀏覽器輸入: http://localhost:8080/monster/list/json
🥦分析+代碼實現(xiàn)
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格式的數(shù)據(jù)* 老師梳理* 1.目標(biāo)方法返回的結(jié)果是給springmvc底層反射調(diào)用的位置使用* 2.我們在springmvc底層反射調(diào)用的位置, 接收到結(jié)果并解析即可* 3.方法上標(biāo)注了@ResponseBody 表示希望以json格式返回給客戶端/瀏覽器* 4.目標(biāo)方法的實參, 在springmvc底層通過封裝好的參數(shù)數(shù)組, 傳入...* @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, 使用它的工具類可以進(jìn)行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 轉(zhuǎn)成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
, 在反射調(diào)用目標(biāo)方法的位置擴展代碼
if (result instanceof String) {String viewName = (String) result;......
}//這里還可以擴展
else if (result instanceof ArrayList) {//如果是ArrayList//判斷目標(biāo)方法是否有@ResponseBody注解Method method = zzwHandler.getMethod();if (method.isAnnotationPresent(ResponseBody.class)) {//把result [ArrayList] 轉(zhuǎn)成json格式數(shù)據(jù)->返回//這里我們需要使用到j(luò)ava中如何將 ArrayList 轉(zhuǎn)成 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.也可以在線轉(zhuǎn)成規(guī)整的json
格式
JSON工具在線解析
3.也可以使用Postman
進(jìn)行測試
🔜 下一篇預(yù)告 🔜
敬請期待:SpringMVC系列九: 數(shù)據(jù)格式化與驗證及國際化
📚 目錄導(dǎo)航 📚
- SpringMVC系列一: 初識SpringMVC
- SpringMVC系列二: 請求方式介紹
- SpringMVC系列三: Postman(接口測試工具)
- SpringMVC系列四: Rest-優(yōu)雅的url請求風(fēng)格
- SpringMVC系列五: SpringMVC映射請求數(shù)據(jù)
- SpringMVC系列六: 視圖和視圖解析器
- SpringMVC系列七: 手動實現(xiàn)SpringMVC底層機制-上
- SpringMVC系列八: 手動實現(xiàn)SpringMVC底層機制-下
…
💬 讀者互動 💬
在學(xué)習(xí)SpringMVC底層機制的過程中,你有哪些疑問或需要幫助的地方?歡迎在評論區(qū)留言,我們一起討論。