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