自定義SpringMVC部分實現

gnng發表於2019-02-26

自定義SpringMVC

1.SpringMVC執行流程

1.1 執行流程圖
複製程式碼

091846_FTTR_3577599.png

1.2 執行過程
複製程式碼
  1. 前端傳送Http請求到DispatcherServlet;
  2. DispatcherServlet收到請求呼叫HandlerMapping處理對映器,處理對映器根據請求的url找到具體的處理器,生成處理器物件以及處理器攔截器(如果有則生成),一併返回給DispatcherServlet;
  3. DispatcherServlet通過HandlerAdapter處理器介面卡呼叫處理器;
  4. 執行處理器(Controlle,也叫後端控制器),返回ModelAndView(資料模型和檢視名稱);
  5. HandlerAdapter將ModelAndView傳給DispatcherServlet;
  6. DispatcherServlet將ModelAndView傳給ViewResolver檢視解析器,解析後返回具體的View;
  7. DispatcherServlet對View進行渲染,將資料模型填充到View中;
  8. DispatcherServlet響應使用者;

2.設計思路

  1. 讀取配置檔案

    SpringMVC本質上是一個Servlet,為了讀取web.xml配置,這裡用到了ServletConfig 這個類,它代表當前Servlet在web.xml中的配置資訊,通過 [^ config.getInitParameter("contextConfigLocation");//讀取啟動引數],讀取application.properties。

            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>application.properties</param-value>
            </init-param>
    複製程式碼
  2. 初始化階段

    @Override
        public void init(ServletConfig config) throws ServletException {
    
            //1.載入配置檔案
            doLoadConfig(config.getInitParameter("contextConfigLocation"));
            //2.初始化相關聯的類,掃描使用者設定包下的所有類
            doScanner(properties.getProperty("scanPackage"));
            //3.拿到掃描到的類,通過反射例項化,並放入IOC容器中,(k-v,beanName-bean),beanName預設首字母小寫
            doInstance();
            //4.初始化HandlerMapping(將url和method對應上)
            iniHandlerMapping();
        }
    複製程式碼
  3. 執行階段

      private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
           if(handlerMapping.isEmpty()){
               return;
           }
      
           String url = req.getRequestURI();
           String contextPath = req.getContextPath();
           url = url.replace(contextPath,"").replaceAll("/+","/");
      
           if(!this.handlerMapping.containsKey(url)){
               resp.getWriter().write("404 No Found");
               return;
           }
           Method method = this.handlerMapping.get(url);
      
           //獲取方法的引數列表
           Class<?>[] parameterTypes = method.getParameterTypes();
      
           //獲取請求的引數
           Map<String, String[]> parameterMap = req.getParameterMap();
      
           //儲存引數值
           Object [] paramValues = new Object[parameterTypes.length];
      
           //方法的引數列表
           for (int i = 0; i < parameterTypes.length; i++) {
               //根據引數名稱,做某些處理
               String requestParam = parameterTypes[i].getSimpleName();
               if("HttpServletRequest".equals(requestParam)){
                   //引數型別已明確,強轉型別是
                   paramValues[i] = req;
                   continue;
               }
               if("HttpServletResponse".equals(requestParam)){
                   paramValues[i] = resp;
                   continue;
               }
               if("String".equals(requestParam)){
                   for (Entry<String, String[]> param : parameterMap.entrySet()) {
                       String valuse = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                       paramValues[i] = valuse;
                   }
               }
           }
      
           //利用反射機制呼叫
           try {
               method.invoke(this.controllerMap.get(url),paramValues);//第一個引數為method所對應的例項,在IOC容器中
           }catch (Exception e){
               e.printStackTrace();
           }
    複製程式碼

3.程式碼實現

github.com/gnng/MySpri…

4.自我總結

  1. Maven的依賴範圍provided

        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <!--    <scope>xxx</scope>依賴範圍
                    * compile,預設值,適用於所有階段,會隨著專案一起釋出。
                    * provided,類似compile,期望JDK、容器或使用者會提供這個依賴。如servlet.jar。
                    * runtime,只在執行時使用,如JDBC驅動,適用執行和測試階段。
                    * test,只在測試時使用,用於編譯和執行測試程式碼。不會隨專案釋出。
                    * system,類似provided,需要顯式提供包含依賴的jar,Maven不會在Repository中查詢它。
                -->
                <scope>provided</scope>
            </dependency>
        </dependencies>
    複製程式碼
  2. Java元註解

    /**
     * java中元註解有四個: @Retention @Target @Document @Inherited;
     * &emsp;&emsp;@Retention:註解的保留位置
     * &emsp;&emsp;&emsp;&emsp;@Retention(RetentionPolicy.SOURCE)   //註解僅存在於原始碼中,在class位元組碼檔案中不包含
     * &emsp;&emsp;&emsp;&emsp;@Retention(RetentionPolicy.CLASS)     // 預設的保留策略,註解會在class位元組碼檔案中存在,但執行時無法獲得,
     * &emsp;&emsp;&emsp;&emsp;@Retention(RetentionPolicy.RUNTIME)  // 註解會在class位元組碼檔案中存在,在執行時可以通過反射獲取到
     *
     * &emsp;&emsp;@Target:註解的作用目標
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.TYPE)   //介面、類、列舉、註解
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.FIELD) //欄位、列舉的常量
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.METHOD) //方法
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.PARAMETER) //方法引數
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.CONSTRUCTOR)  //建構函式
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.LOCAL_VARIABLE)//區域性變數
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.ANNOTATION_TYPE)//註解
     * &emsp;&emsp;&emsp;&emsp;@Target(ElementType.PACKAGE) ///包   
     *  
     *     @Document:說明該註解將被包含在javadoc中
     *  
     * &emsp;  @Inherited:說明子類可以繼承父類中的該註解
     */
    複製程式碼

相關文章