1. 程式人生 > >自己實現spring(二) —— mvc框架的實現

自己實現spring(二) —— mvc框架的實現

  1. DispatchServlet初始化會呼叫initStrategies()方法
protected void initStrategies(ApplicationContext context) {
        //初始化多媒體解析器
        initMultipartResolver(context);
        //初始化語言支援
        initLocaleResolver(context);
        //初始化主題
        initThemeResolver(context);
        //初始化url-->Handler
        initHandlerMappings(context)
; //初始化HandlerAdapter具體的匹配呼叫過程 initHandlerAdapters(context); //初始化呼叫異常處理器 initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); //初始化檢視解析器 initViewResolvers(context); initFlashMapManager(context);
  1. 請求來的時候會呼叫doService(HttpServletRequest request, HttpServletResponse response)方法,然後呼叫doDispatch(request, response);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
    ...
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    ...
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

根據url正則匹配到mappedHandler 然後再拿到HandlerAdapter ha 通過ha反射呼叫具體對應的方法

下面開始擼我們自己的mvc框架,也按照springmvc的套路來

現在web.xml配置我們的servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>com.fanday.mvc.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>applicationContext.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

然後定義我們自己的Adapter和HandlerAdapter類

package com.fanday.mvc;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

public class Handler {

    //呼叫對應的具體controller物件
    private Object controller;
    //和url匹配的方法
    private Method method;
    //對應RequestMapping的url正則
    private Pattern pattern;

    public Handler(Object controller, Method method, Pattern pattern) {
        this.controller = controller;
        this.method = method;
        this.pattern = pattern;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Pattern getPattern() {
        return pattern;
    }

    public void setPattern(Pattern pattern) {
        this.pattern = pattern;
    }
}
package com.fanday.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Map;

public class HandlerAdapter {
    //儲存對應的RequestParam  value==>引數的位置
    //或者HttpRequestServlet.getName()===>index
    private Map<String, Integer> paramType;

    public HandlerAdapter(Map<String, Integer> paramType) {
        this.paramType = paramType;
    }

    /**
     * 具體呼叫的方法
     * @param req
     * @param resp
     * @param handler  和url匹配的handler
     * @throws Exception
     */
    public void handle(HttpServletRequest req, HttpServletResponse resp, Handler handler) throws Exception {
        //獲取要呼叫方法的全部引數型別
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        //建立一個反射呼叫需要的引數值得陣列,陣列長度和引數長度一樣
        Object[] paramValues = new Object[parameterTypes.length];
        /**
         * 如果引數型別-->index  map裡面有HttpServletRequest
         * 就在這個index下的陣列賦值req
         */
        if (paramType.containsKey(HttpServletRequest.class.getName())) {
            paramValues[paramType.get(HttpServletRequest.class.getName())] = req;
        }
        if (paramType.containsKey(HttpServletResponse.class.getName())) {
            paramValues[paramType.get(HttpServletResponse.class.getName())] = resp;
        }

        /**
         * 迴圈遍歷RequestParam  value==>index
         * 如果拿到的value在請求引數裡面有
         * 那麼就從req中取出來賦值給陣列
         * 
         */
        for (Map.Entry<String, Integer> entry : paramType.entrySet()) {
            String paramName = entry.getKey();
            Integer index = entry.getValue();
            //拿到請求name對應的value
            String[] values = req.getParameterValues(paramName);
            if (values != null && values.length != 0){
                String value = Arrays.toString(values).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                //賦值給引數陣列,並且把取出來的string型別轉成我們引數的型別
                paramValues[index] = castValueType(value,parameterTypes[index]);
            }
        }
        //最後反射呼叫Controller的method方法
        handler.getMethod().invoke(handler.getController(),paramValues);
    }

    private Object castValueType(String value, Class<?> clazz) {
        if(clazz == String.class){
            return value;
        }else if(clazz == Integer.class){
            return Integer.valueOf(value);
        }else if(clazz == int.class){
            return Integer.valueOf(value).intValue();
        }else{
            return null;
        }
    }

    public Map<String, Integer> getParamType() {
        return paramType;
    }

    public void setParamType(Map<String, Integer> paramType) {
        this.paramType = paramType;
    }
}

接下來開始寫我們的DisPatchServlet

package com.fanday.mvc.servlet;

import com.fanday.ioc.annotation.Controller;
import com.fanday.ioc.annotation.RequestMapping;
import com.fanday.ioc.annotation.RequestParam;
import com.fanday.ioc.support.AnnotationApplicationContext;
import com.fanday.mvc.Handler;
import com.fanday.mvc.HandlerAdapter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public class DispatcherServlet extends HttpServlet {

    public static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
    private List<Handler> handlerMapping = new ArrayList<Handler>();
    private Map<Handler, HandlerAdapter> adapterMapping = new ConcurrentHashMap<Handler, HandlerAdapter>();

    @Override
    public void init() throws ServletException {
        //取出來web.xml中配置的param引數
        String location = getInitParameter(CONTEXT_CONFIG_LOCATION);
        //建立ApplicationContext上下文,啟動bean的解析  建立  注入等過程
        AnnotationApplicationContext context = new AnnotationApplicationContext(location);

        //請求解析
        initMultipartResolver(context);
        //多語言、國際化
        initLocaleResolver(context);
        //主題View層的
        initThemeResolver(context);

        //解析url和Method的關聯關係
        initHandlerMappings(context);
        //介面卡(匹配的過程)
        initHandlerAdapters(context);

        //異常解析
        initHandlerExceptionResolvers(context);
        //檢視轉發(根據檢視名字匹配到一個具體模板)
        initRequestToViewNameTranslator(context);

        //解析模板中的內容(拿到伺服器傳過來的資料,生成HTML程式碼)
        initViewResolvers(context);

        initFlashMapManager(context);

        System.out.println("GPSpring MVC is init.");
    }

    private void initFlashMapManager(AnnotationApplicationContext context) {
    }

    private void initViewResolvers(AnnotationApplicationContext context) {
    }

    private void initRequestToViewNameTranslator(AnnotationApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(AnnotationApplicationContext context) {
    }

    private void initHandlerAdapters(AnnotationApplicationContext context) {
        if (handlerMapping.isEmpty()) return;
        //遍歷所有的handlerMapping
        for (Handler handler : handlerMapping) {
            Method method = handler.getMethod();
            //建立一個儲存RequestParam 註解的value(即引數名)==>index(引數位置索引)
            Map<String, Integer> paramType = new HashMap<String, Integer>();
            //獲取所有的引數型別陣列
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> type = parameterTypes[i];
                //如果有HttpServletRequest型別就往map中儲存 型別名==>index
                if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                    paramType.put(type.getName(),i);
                }
            }

            //獲取所有的引數註解,之所以返回二維陣列,是因為每個引數可能有
            //多個註解修飾
            Annotation[][] pas = method.getParameterAnnotations();
            for (int i = 0; i < pas.length; i++) {
                //獲取第i個引數的修飾註解陣列
                Annotation[] pa = pas[i];
                //遍歷每個引數的修飾註解
                for (Annotation a:pa){
                    if(a instanceof RequestParam){
                        String paramName = ((RequestParam) a).value();
                        if(!"".equals(paramName)){
                            //如果註解屬於@RequestParam
                            //把註解引數 name==>index儲存map
                            paramType.put(paramName,i);
                        }
                    }
                }
            }
            adapterMapping.put(handler,new HandlerAdapter(paramType));
        }

    }

    /**
     * 初始化handlerMappings
     * @param context
     */
    private void initHandlerMappings(AnnotationApplicationContext context) {
        //獲取context中所有的bean
        Map<String, Object> beans = context.getBeans();
        if (beans.isEmpty()) return;
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //只對controller修飾的類做解析
            if (!entry.getValue().getClass().isAnnotationPresent(Controller.class)) return;
            String url = "";
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(RequestMapping.class)) {
                //先取類上面的url
                url = clazz.getAnnotation(RequestMapping.class).value();
            }

            Method[] methods = clazz.getMethods();
            //再取對應方法上的url
            for (Method m : methods) {
                if (!m.isAnnotationPresent(RequestMapping.class)) continue;
                String subUrl = m.getAnnotation(RequestMapping.class).value();
                String regex = (url + subUrl).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);
                //新增到handlerMapping中去
                handlerMapping.add(new Handler(entry.getValue(), m, pattern));
            }

        }
    }

    private void initThemeResolver(AnnotationApplicationContext context) {
    }

    private void initLocaleResolver(AnnotationApplicationContext context) {
    }

    private void initMultipartResolver(AnnotationApplicationContext context) {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //取出匹配的handler
        Handler handler = getHandler(req);

        //根據handler取出HandlerAdapter
        HandlerAdapter ha = getHandlerAdapter(handler);

        //呼叫handle方法處理請求,暫時未做ModalAndView處理
        ha.handle(req,resp,handler);
    }

    private HandlerAdapter getHandlerAdapter(Handler handler) {
        if(adapterMapping.isEmpty())return null;
        return adapterMapping.get(handler);
    }

    private Handler getHandler(HttpServletRequest req) {
        if(handlerMapping.isEmpty())return null;
        String contextPath = req.getContextPath();
        String url = req.getRequestURI();
        //獲取請求的url  除去contextPath剩餘的
        url = url.replace(contextPath,"").replaceAll("/+","/");
        for (Handler handler:handlerMapping){
            if(handler.getPattern().matcher(url).matches()){
                //匹配到就把handler返回
                return handler;
            }
        }
        return null;
    }
}

測試我們的mvc框架

這裡寫圖片描述

至此mvc框架也完成了,剩餘的就是模板引擎的處理了,說一下模板的大致思想:

  1. 呼叫ha.handle(req,resp,handler);方法之後會返回ModalAndView
  2. 判斷handler的method方法返回值型別,如果是ModalAndView,就把method.invoke()返回的值強轉成ModalAndView
  3. 取出來view就是對應模板的name,把模板讀出來,正則找到我們定義的例如:@{name}這種,用modal中值進行替換,resp輸出
  4. 如果返回的是string,就判斷method上面的註解有沒有@ResponseBody,有的話resp輸出json資料

相關推薦

自己實現spring() —— mvc框架實現

DispatchServlet初始化會呼叫initStrategies()方法 protected void initStrategies(ApplicationContext context) { //初始化多媒體解析器

RESTful風格(使用Ajax+Spring MVC框架實現)

一、環境配置 ①、開發前的基礎配置:配置好相應的Spring MVC、JSON和Restful需要的依賴程式包。 ②、Tomcat9.0伺服器配置 ③、在專案配置web.xml <servlet-name

C#使用MVC框架實現登陸驗證

ring 數據庫訪問 傳輸數據 圖片 ont resource 使用 如果 database 步驟一:需求分析 我的目標是利用MVC框架實現簡單登陸驗證。從客戶端輸入用戶名和密碼。然後傳給數據庫驗證。如果數據庫存在此用戶名ID和密碼,則返回客戶端賬戶姓名的成功提示。否則返回

Spring整合Quartz框架實現定時任務跑批(Maven完整版)

觸發器 delay cut www 方法 lin job 定時任務 任務調度 Quartz 介紹Quartz is a full-featured, open source job scheduling service that can be integrated with

Spring整合Quartz框架實現分散式定時任務

1、叢集使用定時任務的問題:    目前大部分在叢集中處理定時任務的方式不是正真的分散式處理方式,而是一種偽分散式,這種方式存在一個明顯的缺陷就是當叢集中機器宕機, 那麼整個定時任務就會掛掉或者不能一次性跑完,會對業務產生嚴重的影響。   而且在叢集環境中,

java實現簡單的MVC框架

.title { background: blue; height: 50px; line-height: 50px; border: 1px solid; color: red } 一、mvc的模式如下圖所示 二、基於路徑訪問的控制器 控制器BaseServlet類如下 package c

STL綜合例項 打分系統(框架實現

接上篇 注意分數score是陣列   #include<iostream> #include<map> #include<vector> #include<string> #include<iterator> #inclu

寫一個屬於自己的PHP的MVC框架

第一篇文章已經把所需的目錄搭建好了,接下來的工作就是寫一些程式碼了 用編輯器開啟public/index.php檔案,寫上下面的程式碼 <?php define(DS, DIRECTORY_SEPARATOR); define(ROOT, dirna

.net mvc 框架實現後臺管理系統 3

左側選單實現 預設html <div class="layui-side layui-bg-black h-slide"> <div class="layui-side-scroll"> <ul class="layui-nav layui-nav-tree" lay-fil

一步步實現web程式資訊管理系統之--後臺框架實現跳轉登陸頁面

SpringBoot springboot的目的是為了簡化spring應用的開發搭建以及開發過程。內部使用了特殊的處理,使得開發人員不需要進行額外繁鎖的xml檔案配置的編寫,其內部包含很多模組的配置只需要新增maven依賴即可使用,這項功能可謂對開發人員提供了大大的好處。使用springboot

一步一步實現web程式資訊管理系統之----後臺框架實現跳轉登陸頁面

SpringBoot springboot的目的是為了簡化spring應用的開發搭建以及開發過程。內部使用了特殊的處理,使得開發人員不需要進行額外繁鎖的xml檔案配置的編寫,其內部包含很多模組的配置只需要新增maven依賴即可使用,這項功能可謂對開發人員提供了大大的好處。使用springboot只需要簡單配置

一步一步實現web程序信息管理系統之----後臺框架實現跳轉登陸頁面

frame tco 代碼 pom web 放置 異常 boot ase SpringBootspringboot的目的是為了簡化spring應用的開發搭建以及開發過程。內部使用了特殊的處理,使得開發人員不需要進行額外繁鎖的xml文件配置的編寫,其內部包含很多模塊的配置只需要

.net mvc 框架實現後臺管理系統4-layedit使用

layer spl ima art mage get image none UNC 做個簡單的文章發布,使用了layui的layedit 效果: 在html頁面添加: <textarea id="MyArticleContent" style="display: n

JQuery Ajax 結合.net MVC框架實現頁面區域性重新整理

JQuery Ajax的非同步重新整理可實現html靜態頁面率先載入完畢,呈現給使用者,對提升使用者體驗很有幫助.本文舉一個小例子,在.net的MVC框架下面實現JQuery Ajax的非同步重新整理. 我們模擬一個場景,在頁面載入完畢後,通過Ajax獲取使用

Spring整合aspectj框架實現的aop

Spring整合aspectj框架實現的aop 在現在的開發中使用這種方案比較多. 在spring2.0以後它支援jdk1.5註解,而整合aspectj後可以使用aspectj語法,可以簡化開發。 Aspect:切面 =切點+通知(多個切點與多個通知的組合) Aspec

自己實現spring(一) —— ioc容器實現

前言:加深對spring的理解,把spring ioc的大致流程抽出來,自己動手擼一個簡潔版的 新建一個maven工程,這裡使用idea工具 pom檔案只依賴一個servlet <?xml version="1.0" encodi

MVC框架實現1---Smart框架簡介

Smart我是寫的簡易MVC框架,其工作模式類似於struts1、struts2,之所以命名為Smart是因為我覺得MVC框架都應具有精巧、靈活的特性,而這個單詞恰能夠表達這個意思。 Smart框架的整體架構模仿了Struts1,但在Action元件的設計方面模仿了Stru

Servlet3.0實現的簡單mvc框架

jar包準備:servlet-api-3.0.jar 專案結構如下: 註解Mapping ,用於對映url地址 /* * 檔名:Action.java * 版權:Copyright 2007-2016 517na Tech. Co. Ltd.

一個簡單 Go Web MVC 框架實現思路

 需要的知識點    為了防止你的心裡不適,需要以下知識點: Go 基本知識 Go 反射的深入理解 使用過框架  Go Web 伺服器搭建 package main import ( "fmt" "net/http" ) func do(w http.ResponseWriter,

自己寫“掃雷”().業務邏輯實現

這個時候我們需要處理一下我們的業務邏輯了! 在這裡先簡略滴說說業務邏輯的思路: 主要是用一個二維陣列來表示這些格子,status表示是否點選,或者標記為雷,hasLandmine記錄這個格子是否有雷 每次繪圖的時候根據這幾個陣列來判斷應該載入哪種點陣圖就可以了!蠻簡單的!