手動實現(xiàn) SpringMVC

先看整體架構

因為我們這個 MVC 框架要依賴 IOC/DI 容器谬擦,所以我們在 pom 文件里要將自己的 Spring 框架引入進來薯演。

實現(xiàn) MVC 的整體功能

首先我們定義兩個 MVC 專用的注解旧蛾,RequestMapping 用來做 url 匹配咸作,RequestParam 做參數(shù)轉換:

packagecom.paul.annotation;importjava.lang.annotation.*;@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestMapping {Stringvalue()default"";}

packagecom.paul.annotation;importjava.lang.annotation.*;@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceRequestParam {Stringvalue()default"";}

我們知道 SpringMVC 的核心是 DispatcherServlet浙值,用來做核心路由控制羔巢,我們也定義這樣一個類萝映,并且在初始化方法里初始化一個 IOC/DI 容器吴叶,看過前面文章的同學應該知道,初始化容器后我們已經(jīng)將 Bean 放到容器中而且完成了依賴注入序臂。

package com.paul.servlet;importcom.paul.annotation.RequestMapping;importcom.paul.annotation.RequestParam;importorg.springframework.ioc.annotation.Controller;importorg.springframework.ioc.bean.AnnotationApplicationContext;importorg.springframework.ioc.bean.ApplicationContext;importjavax.servlet.ServletConfig;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.lang.annotation.Annotation;importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;//實現(xiàn) servlet蚌卤,和我們以前使用 servlet 一樣。publicclassDispatcherServletextendsHttpServlet{// 完整路徑和 方法的 mappingprivateMap handleMapping =newConcurrentHashMap<>();// 類路徑和controller 的 mappingprivateMap controllerMapping =newConcurrentHashMap<>();? ? ? ? privateMap beanFactory =newConcurrentHashMap<>();? ? @Override? ? publicvoidinit(ServletConfig config) throws ServletException {super.init(config);//實例化 IOC 容器ApplicationContext ctx =newAnnotationApplicationContext("applicationContext.xml");this.beanFactory = ((AnnotationApplicationContext) ctx).beanFactory;//上一步已經(jīng)完成了 Controller奥秆,service逊彭,respostry,autowired 等注解的掃描和注入//遍歷容器构订,將 requestmapping 注解的路徑和對應的方法以及 contoller 實例對應起來for(Map.Entry entry:beanFactory.entrySet()){Objectinstance = entry.getValue();? ? ? ? ? ? Class clazz = instance.getClass();if(clazz.isAnnotationPresent(Controller.class)){? ? ? ? ? ? ? ? RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);StringclassPath = requestMapping.value();? ? ? ? ? ? ? ? Method[] methods = clazz.getMethods();for(Method method:methods){if(method.isAnnotationPresent(RequestMapping.class)){? ? ? ? ? ? ? ? ? ? ? ? RequestMapping requestMapping2 = method.getAnnotation(RequestMapping.class);StringmethodPath = requestMapping2.value();StringrequestPath = classPath + methodPath;? ? ? ? ? ? ? ? ? ? ? ? handleMapping.put(requestPath,method);? ? ? ? ? ? ? ? ? ? ? ? controllerMapping.put(requestPath,instance);? ? ? ? ? ? ? ? ? ? }else{continue;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }else{continue;? ? ? ? ? ? }? ? ? ? }? ? }? ? @Override? ? protectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);? ? }//根據(jù)上一步獲取到的 mapping侮叮,根據(jù) url 找到對應的 controller 和方法去執(zhí)行。@Override? ? protectedvoiddoPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Stringuri = req.getRequestURI();//? /paul-mvc/com.paul.controller/method-com.paul.controllerStringcontext = req.getContextPath();// /paul-vmcStringpath = uri.replace(context,"");// /com.paul.controller/method-com.paul.controllerMethod m = (Method) handleMapping.get(path);//從容器里拿到controller 實例Objectinstance = controllerMapping.get(path);Object[] args =? handle(req,resp,m);for(Objecta:args){? ? ? ? ? ? System.out.println("Object:"+a);? ? ? ? }try{? ? ? ? ? ? m.invoke(instance,args);? ? ? ? }catch(IllegalAccessException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }catch(InvocationTargetException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }? ? privatestaticObject[] handle(HttpServletRequest req, HttpServletResponse resp,Method method){//拿到當前執(zhí)行的方法有哪些參數(shù)Class[] paramClazzs = method.getParameterTypes();//根據(jù)參數(shù)的個數(shù)悼瘾,new 一個參數(shù)的數(shù)據(jù)Object[] args =newObject[paramClazzs.length];? ? ? ? int args_i =0;? ? ? ? int index =0;for(Class paramClazz:paramClazzs){if(ServletRequest.class.isAssignableFrom(paramClazz)){? ? ? ? ? ? ? ? args[args_i++] = req;? ? ? ? ? ? }if(ServletResponse.class.isAssignableFrom(paramClazz)){? ? ? ? ? ? ? ? args[args_i++] = resp;? ? ? ? ? ? }//判斷requestParam? 注解Annotation[] paramAns = method.getParameterAnnotations()[index];if(paramAns.length >0){? ? ? ? ? ? ? ? System.out.println("my");for(Annotation paramAn:paramAns){if(RequestParam.class.isAssignableFrom(paramAn.getClass())){? ? ? ? ? ? ? ? ? ? ? ? System.out.println("13mj");? ? ? ? ? ? ? ? ? ? ? ? RequestParam rp = (RequestParam) paramAn;? ? ? ? ? ? ? ? ? ? ? ? args[args_i++] = req.getParameter(rp.value());? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? ? ? index ++;? ? ? ? }returnargs;? ? }}

測試代碼

新建一個 WebDemo,Maven web 項目肚医。

先來看我們需要測試的 Controller 和 Service轿腺。

@Controller@RequestMapping("/user")publicclassUserController{@AutowiredprivateUserService userService;@RequestMapping("/query")publicvoidget(HttpServletRequest request, HttpServletResponse response, @RequestParam("name")String name,@RequestParam("age")String age){try{? ? ? ? ? ? PrintWriter pw = response.getWriter();? ? ? ? ? ? String res = userService.query(name,age);? ? ? ? ? ? pw.write(res);? ? ? ? }catch(IOException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }}

importorg.springframework.ioc.annotation.Service;@Service("userService")publicclassUserServiceImplimplementsUserService{@OverridepublicStringquery(String name, String age){return"name="+name+"age="+age;? ? }}

在 resources 目錄我們需要寫一個名字為 applicationContext 的配置文件來指明掃描包路徑。

<?xml version="1.0"encoding="UTF-8"?>

在 web.xml 中配置我們自己的 DispatcherServlet歹嘹。

<!DOCTYPE web-app PUBLIC

? ? ? ? "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

? ? ? ? "http://java.sun.com/dtd/web-app_2_3.dtd" >Archetype Created Web ApplicationDispatcherServletcom.paul.servlet.DispatcherServlet0DispatcherServlet/

在瀏覽器中測試結果:

結果和我們想的一樣。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孔庭,一起剝皮案震驚了整個濱河市尺上,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌圆到,老刑警劉巖怎抛,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芽淡,居然都是意外死亡马绝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門挣菲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來富稻,“玉大人,你說我怎么就攤上這事白胀⊥指常” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長向抢。 經(jīng)常有香客問我挟鸠,道長,這世上最難降的妖魔是什么茉盏? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任鸠姨,我火速辦了婚禮讶迁,結果婚禮上核蘸,老公的妹妹穿的比我還像新娘。我一直安慰自己祟峦,他們只是感情好宅楞,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布厌衙。 她就那樣靜靜地躺著,像睡著了一般婶希。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彤枢,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天奕塑,我揣著相機與錄音,去河邊找鬼。 笑死讨衣,一個胖子當著我的面吹牛反镇,可吹牛的內(nèi)容都是我干的歹茶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼揩页!你這毒婦竟也來了爆侣?” 一聲冷哼從身側響起兔仰,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤乎赴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缔赠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡友题,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了度宦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡戈抄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出划鸽,到底是詐尸還是另有隱情输莺,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布裸诽,位于F島的核電站嫂用,受9級特大地震影響,放射性物質發(fā)生泄漏嘱函。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓄氧。 院中可真熱鬧匀们,春花似錦、人聲如沸重抖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恨统。三九已至叁扫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間畜埋,已是汗流浹背莫绣。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悠鞍,地道東北人对室。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像咖祭,于是被迫代替她去往敵國和親掩宜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring框架對于Java后端程序員來說再熟悉不過了么翰,以前只知道它用的反射實現(xiàn)的牺汤,但了解之后才知道有很多巧妙的設...
    Java小鋪閱讀 1,358評論 3 23
  • 前幾章我們已經(jīng)分析了 Spring 的源碼并且手動實現(xiàn)了一個 IOC/DI 容器。 這章我們在自己實現(xiàn)的 Spri...
    當年明月_3025閱讀 482評論 0 0
  • 這部分主要是與Java Web和Web Service相關的面試題浩嫌。 96檐迟、闡述Servlet和CGI的區(qū)別? 答...
    雜貨鋪老板閱讀 1,404評論 0 10
  • 作為java程序員,項目中使用到的主流框架多多少少和spring有關聯(lián)码耐,在面試的過程難免會問一些spring sp...
    adminkkk閱讀 573評論 0 1
  • 游子吟 孟郊 慈母手中線锅减,游子身上衣。 臨行密密縫伐坏,意恐遲遲歸。 誰言寸草心握联,報得三春暉桦沉。
    顧勇詩書閱讀 241評論 0 5