自己实现spring核心功能 三
前言
?前兩篇已經(jīng)基本實(shí)現(xiàn)了spring的核心功能,下面講到的參數(shù)綁定是屬于springMvc的范疇了。本篇主要將請(qǐng)求到servlet后怎么去做映射和處理。首先來(lái)看一看dispatherServlet的基本流程,這我在以前的博客里面也講過(guò),傳送門(mén)
這里先給個(gè)我們的簡(jiǎn)易處理流程
?
準(zhǔn)備工作
?為了能將請(qǐng)求傳遞,我們需要寫(xiě)一個(gè)控制器類(lèi)來(lái)接收請(qǐng)求,寫(xiě)兩個(gè)接口來(lái)處理請(qǐng)求
HomeController類(lèi) 1 @JCController 2 @JCRequestMapping("/home") 3 public class HomeController { 4 @JCAutoWrited 5 private IHomeService homeService; 6 7 @JCRequestMapping("/sayHi") 8 public String sayHi() { 9 return homeService.sayHi(); 10 } 11 12 @JCRequestMapping("/getName") 13 public String getName(Integer id,String no) { 14 return homeService.getName(id,no); 15 } 16 @JCRequestMapping("/getRequestBody") 17 public String getRequestBody(Integer id, String no, GetUserInfo userInfo) { 18 return homeService.getRequestBody(id,no,userInfo); 19 } 20 } View Code HomeService類(lèi) 1 @JCService 2 public class HomeService implements IHomeService{ 3 4 @JCAutoWrited 5 StudentService studentService; 6 @Override 7 public String sayHi() { 8 return studentService.sayHi(); 9 } 10 11 @Override 12 public String getName(Integer id,String no) { 13 return "SB0000"+id; 14 } 15 16 @Override 17 public String getRequestBody(Integer id, String no, GetUserInfo userInfo) { 18 return "userName="+userInfo.getName()+" no="+no; 19 } 20 } View Code StudentService類(lèi) 1 @JCService 2 public class StudentService implements IStudentService{ 3 @Override 4 public String sayHi(){ 5 return "Hello world!"; 6 } 7 } View Code?
無(wú)參請(qǐng)求過(guò)程
根據(jù)上面的圖,我們?cè)跒g覽器發(fā)起請(qǐng)求localhost:8080/home/sayHi,請(qǐng)求會(huì)到達(dá)JCDispatherServlet類(lèi),由于我們是GET請(qǐng)求
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatcherServlet(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
會(huì)走到doDispatcherServlet方法里面處理請(qǐng)求
1 void doDispatcherServlet(HttpServletRequest req, HttpServletResponse resp) throws Exception { 2 String url = req.getRequestURI(); 3 url = url.replace(req.getContextPath(), "").replaceAll("/+", "/"); 4 if (!urlMapping.containsKey(url)) { 5 resp.getWriter().write("404! url is not found!");` 6 return; 7 } 8 9 Method method = urlMapping.get(url); 10 String className = 11 method.getDeclaringClass().getSimpleName(); 12 className = firstLowerCase(className); 13 if (!ioc.containsKey(className)) { 14 resp.getWriter().write("500! claas not defind !"); 15 return; 16 } 17 Object[] args=null ; 18 19 //調(diào)用目標(biāo)方法 20 Object res = method.invoke(ioc.get(className), args); 21 22 resp.setContentType("text/html;charset=utf-8"); 23 resp.getWriter().write(res.toString()); 24 }第九行代碼會(huì)以u(píng)rl為key從HashMap取出數(shù)據(jù),返回Method對(duì)象,它對(duì)應(yīng)到我們?cè)贖omeController中定義的方法public String sayHi() 。
public Object invoke(Object obj, Object... args)通過(guò)反射調(diào)用方法,需要2個(gè)參數(shù),第一個(gè)是方法所在類(lèi)的對(duì)象,一個(gè)是方法所需要的參數(shù)。
下面的代碼就是在ioc容器中取HomeController類(lèi)對(duì)象,如果沒(méi)有,就拋500錯(cuò)誤。
Method method = urlMapping.get(url);String className = method.getDeclaringClass().getSimpleName();
className = firstLowerCase(className);
if (!ioc.containsKey(className)) {
resp.getWriter().write("500! claas not defind !");
return;
}
//調(diào)用目標(biāo)方法
Object res = method.invoke(ioc.get(className), null); resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(res.toString());
?
可以看到,無(wú)參請(qǐng)求args傳過(guò)去null,然后把調(diào)用結(jié)果返回,瀏覽器打印結(jié)果,證明無(wú)參可以使用了。
接下來(lái)就需要接收參數(shù)的傳遞了。
參數(shù)傳遞
?我們常用參數(shù)傳遞大致分為三種,GET傳參,POST方式form傳參,POST方式j(luò)son傳參。
?
? 前兩種傳參都可以通過(guò)HttpServletRequest的getParameterMap()獲取到,它返回的是Map<String, String[]>結(jié)構(gòu),我們需要遍歷map,拿到里面的key和值。
@JCRequestMapping("/getName")public String getName(Integer id,String no) {return homeService.getName(id,no);}我們?cè)跒g覽器請(qǐng)求輸入http://localhost:8080/home/getName?id=11&no=lisi
傳過(guò)來(lái)的是這樣一個(gè)數(shù)據(jù),里面有字段名稱(chēng),有字段的value
?而json格式的參數(shù),需要從輸入流里面獲取?req.getInputStream()?
?知道了傳進(jìn)來(lái)的字段名稱(chēng)后,現(xiàn)在有2個(gè)問(wèn)題,一個(gè)是方法參數(shù)的類(lèi)型,一個(gè)是方法參數(shù)的順序,這就涉及到了參數(shù)的綁定
?
參數(shù)綁定
?什么叫參數(shù)綁定,舉個(gè)例子
從這個(gè)方法的聲明,可以看到,第一個(gè)參數(shù)要求是名稱(chēng)為id且類(lèi)型為Integer,第二個(gè)參數(shù)要求名稱(chēng)為no且類(lèi)型為String。
我們需要把request傳進(jìn)來(lái)的參數(shù)列表,按照方法的要求,一個(gè)一個(gè)傳進(jìn)去,不能少也不能類(lèi)型錯(cuò)亂。
要完成這個(gè)要求,我們首先需要獲得方法的形參列表,其次要把參數(shù)按順序,按類(lèi)型給組裝好。
1.獲取形參列表
2.按要求組裝好參數(shù)
?
獲取形參列表
從這里可以看到?獲取到的參數(shù)個(gè)數(shù)是正常的,類(lèi)型也沒(méi)有問(wèn)題,但字段名稱(chēng)顯然是錯(cuò)誤的,咱們正確的字段名稱(chēng)應(yīng)該是id、no
通過(guò)網(wǎng)上可以查到,這個(gè)需要達(dá)到3個(gè)要求才能正常使用。
1.jdk1.8?
2.在idea設(shè)置參數(shù) -parameters
3.Build->RebuildProject
?設(shè)置方法博客地址
?還有個(gè)前提,每次JCDispatherServlet代碼變更,需要重新編譯項(xiàng)目Build->RebuildProject?生成最新的代碼
?重新編譯后,調(diào)試結(jié)果如下
到此獲取形參的工作已經(jīng)做好了,只需要循環(huán)parameters數(shù)組就好了。
類(lèi)型轉(zhuǎn)換
不過(guò)還有個(gè)比較棘手的問(wèn)題
我們發(fā)現(xiàn),從request獲取到的實(shí)參都是String數(shù)組類(lèi)型,需要根據(jù)形參轉(zhuǎn)成指定類(lèi)型,而且只能通過(guò)反射轉(zhuǎn)換。
?所以一番折騰后,把入?yún)⑥D(zhuǎn)換成指定類(lèi)型的實(shí)參的代碼如下
1 Object getInstanceField(Parameter parameter, String value) { 2 if (parameter.getName().isEmpty()) { 3 return null; 4 } 5 Class typeClass = parameter.getType(); 6 Constructor con = null; 7 try { 8 con = typeClass.getConstructor(value.getClass()); 9 return con.newInstance(value); 10 } catch (InvocationTargetException e) { 11 e.printStackTrace(); 12 } catch (IllegalAccessException e) { 13 e.printStackTrace(); 14 } catch (InstantiationException e) { 15 e.printStackTrace(); 16 } catch (NoSuchMethodException e) { 17 e.printStackTrace(); 18 } 19 return null; 20 } 21 22 Object[] doPostParam(HttpServletRequest req, Method method) { 23 Parameter[] parameters = method.getParameters(); 24 Object[] requestParam = new Object[parameters.length]; 25 int i = 0; 26 for (Parameter p : parameters) { 27 requestParam[i] = null; 28 if (!p.getName().isEmpty()) { 29 requestParam[i] = getInstanceField(p, req.getParameter(p.getName())); 30 } 31 i++; 32 } 33 return requestParam; 34 } 35 36 Object[] doJsonParam(String json, Method method) { 37 if (null == json || json.isEmpty()) { 38 return null; 39 } 40 Parameter[] parameters = method.getParameters(); 41 Object[] requestParam = new Object[parameters.length]; 42 JSONObject jsonObject = JSONObject.parseObject(json); 43 int i = 0; 44 for (Parameter p : parameters) { 45 Object val = jsonObject.getObject(p.getName(), p.getType()); 46 requestParam[i] = val; 47 i++; 48 } 49 return requestParam; 50 } 51 52 Object[] doGetParam(Map<String, String[]> map, Method method) { 53 if (null == map || map.size() == 0) { 54 return null; 55 } 56 Parameter[] parameters = method.getParameters(); 57 int i = 0; 58 Object[] requestParam = new Object[parameters.length]; 59 for (Parameter p : parameters) { 60 requestParam[i] = null; 61 if (map.containsKey(p.getName())) { 62 String[] values = map.get(p.getName()); 63 requestParam[i] = getInstanceField(p, values[0]); 64 } 65 i++; 66 } 67 return requestParam; 68 } View Code?
返回Object[],里面的類(lèi)型和順序需要保證準(zhǔn)確?
瀏覽器調(diào)用結(jié)果Get請(qǐng)求
?
也可以用PostMan?發(fā)起Post請(qǐng)求
?
?這兩種都每辦法傳對(duì)象,而我們開(kāi)發(fā)者需要傳遞對(duì)象。所以,再加個(gè)接口,測(cè)試包含對(duì)象時(shí)的混合綁定
?
對(duì)象類(lèi)型參數(shù)綁定
?
@JCRequestMapping("/getRequestBody")public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {return homeService.getRequestBody(id,no,userInfo);}doDispatcherServlet() 處理請(qǐng)求需要根據(jù)請(qǐng)求方式做不同處理,改造后如下 1 Object[] args; 2 if ("GET".equalsIgnoreCase(req.getMethod())) { 3 args = doGetParam(req.getParameterMap(), method); 4 } else if ("POST".equalsIgnoreCase(req.getMethod()) && req.getContentType().contains("json")) { 5 String str = getJson(req); 6 args = doJsonParam(str, method); 7 } else { 8 args = doPostParam(req, method); 9 } 10 //調(diào)用目標(biāo)方法 11 Object res = method.invoke(ioc.get(className), args);
傳對(duì)象只能通過(guò)json方式傳進(jìn)來(lái),所以我們postMan請(qǐng)求json格式數(shù)據(jù)
處理json請(qǐng)求
| 請(qǐng)求地址 | 請(qǐng)求方式 | 請(qǐng)求參數(shù) |
| http://localhost:8080/home/getRequestBody | application/json | {"id":11,"no":"SB00011","userInfo":{"name":"小提莫","age":20}} |
?
?
?
json請(qǐng)求核心代碼就是使用fastjson根據(jù)字段名取值
1 Object[] doJsonParam(String json, Method method) { 2 if (null == json || json.isEmpty()) { 3 return null; 4 } 5 Parameter[] parameters = method.getParameters(); 6 Object[] requestParam = new Object[parameters.length]; 7 JSONObject jsonObject = JSONObject.parseObject(json); 8 int i = 0; 9 for (Parameter p : parameters) { 10 Object val = jsonObject.getObject(p.getName(), p.getType()); 11 requestParam[i] = val; 12 i++; 13 } 14 return requestParam; 15 }?返回結(jié)果:
?結(jié)束
到這里,servlet處理請(qǐng)求,并響應(yīng)已經(jīng)得到驗(yàn)證,能夠正常的對(duì)外提供服務(wù)。一個(gè)微型的springMvc框架已經(jīng)完成了。
完整代碼
轉(zhuǎn)載于:https://www.cnblogs.com/jingch/p/11378510.html
總結(jié)
以上是生活随笔為你收集整理的自己实现spring核心功能 三的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Go 循环控制
- 下一篇: 将字符串的首字母变为大写