1. 程式人生 > >springMVC學習心得及手寫springMVC簡單實現

springMVC學習心得及手寫springMVC簡單實現

springMVC學習心得及手寫springMVC簡單實現

Spring 是一個企業級開發框架,為解決企業級專案開發過於複雜而建立的,框架的主要優勢之一就是分層架構,允許開發者自主選擇元件。

Spring 的兩大核心機制是 IoC(控制反轉)和 AOP(面向切面程式設計),從開發的角度講,我們使用 Spring 框架就是用它的 IoC 和 AOP。

什麼是AOP和IOC

IoC 是典型的工廠模式,通過工廠去注入物件
AOP 是代理模式的體現
  • IOC也叫控制反轉 ,控制反轉是什麼意思呢?反轉的的誰? 在傳統開發中一半是有呼叫者建立物件例項,也就是是呼叫者主動new出來的,那麼IOC就是將這個建立物件例項的任務從呼叫者的手裡反轉到spring的IOC容器來執行,控制反轉主要是反轉的這個過程;
    舉個例子:
    傳統模式:以前去買菜需要自己帶籃子,去超市買完自己帶回家;
    IOC模式:空手去超市就可以了,籃子超市已經給你準備好了,直接拿來用就好了.
  • 依賴注入(Dependency Injection,DI) 能實現在程式執行中,動態地向某個物件提供它所依賴的其它物件的功能。

SpringMVC的常用元件及功能

  • DispatcherServlet: 前端控制器,是整個流程控制的核心,控制其他元件的執行,統一排程,降低元件之間的耦合性,相當於總指揮。
  • Handler:處理器,完成具體業務邏輯,相當於 Servlet 或 Action。
  • HandlerMapping: DispatcherServlet 接收到請求之後,通過 HandlerMapping 將不同的請求分發到不同的 Handler。
  • HandlerInterceptor:處理器攔截器,是一個介面,如果我們需要做一些攔截處理,可以來實現這個介面。
  • HandlerExecutionChain:處理器執行鏈,包括兩部分內容,Handler 和 HandlerInterceptor(系統會有一個預設的 HandlerInterceptor,如果需要額外攔截處理,可以新增攔截器設定)。
  • HandlerAdapter:處理器介面卡,Handler 執行業務方法之前,需要進行一系列的操作,包括表單資料的驗證、資料型別的轉換、將表單資料封裝到 JavaBean 等,這一系列的操作,都是由 HandlerAdapter 來完成,- DispatcherServlet 通過 HandlerAdapter 執行不同的 Handler。
  • ModelAndView:裝載了模型資料和檢視資訊,作為 Handler 的處理結果,返回給 DispatcherServlet。
  • ViewResolver:檢視解析器,DispatcherServlet 通過它將邏輯檢視解析成物理檢視,最終將渲染結果響應給客戶端。

Spring MVC 的實現流程

  • 客戶端請求被 DispatcherServlet(前端控制器)接收

  • 根據 HandlerMapping 對映到 Handler
    生成 Handler 和 HandlerInterceptor(如果有則生成)

  • Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一併返回給 DispatcherServlet

  • DispatcherServlet 通過 HandlerAdapter 呼叫 Handler 的方法做業務邏輯處理
    返回一個 ModelAndView 物件給 DispatcherServlet

  • DispatcherServlet 將獲取的 ModelAndView 物件傳給 ViewResolver 檢視解析器,將邏輯檢視解析成物理檢視 View

  • ViewResolver 返回一個 View 給 DispatcherServlet

  • DispatcherServlet 根據 View 進行檢視渲染(將模型資料填充到檢視中)

  • DispatcherServlet 將渲染後的檢視響應給客戶端

  • 在這裡插入圖片描述
    看到上面的實現原理,可能會有這樣的擔心,Spring MVC 如此眾多的元件開發起來一定很麻煩吧?答案是否定的,Spring MVC 使用起來非常簡單,很多元件都由框架提供,作為開發者我們直接使用即可,並不需要自己手動編寫程式碼,真正需要開發者進行編寫的元件只有兩個:

  • Handler,處理業務邏輯

  • View,JSP 做展示

Spring MVC 底層實現

一個 Spring MVC 框架,相比於 MyBatis 框架,Spring MVC 要簡單一些,只需要 XML 解析 + 反射就可以完成,不需要 JDK 動態代理。 強調文字

下面,來手寫一下springMVC的簡單實現,廢話不多說,直接上乾貨

要自己寫框架,必須理解框架的底層原理和執行機制,這部分在上一部分已經講過

1.MyDispatcherServlet

    首先需要一個前置控制器 DispatcherServlet,作為整個流程的核心,由它去呼叫其他組
件,共同完成業務。主要元件有兩個:一是 Controller,呼叫其業務方法 Method,執行業務邏輯;
二是 ViewResolver 檢視解析器,將業務方法的返回值解析為物理
檢視 + 模型資料,返回客戶端。
我們自己寫框架就按照這個思路來。

初始化工作

  • 根據 Spring IoC 的思路,需要將參與業務的物件全部建立並儲存,供流程呼叫。因此,首先需要建立 Controller 物件,HTTP 請求是通過註解找到對應的 Controller 物件,我們需要將所有的 Controller 與其註解建立關聯,很顯然,使用 key-value 結構的 Map 集合來儲存最合適不過了,這樣就模擬了 IoC 容器。

  • Controller 的 Method 也是通過註解與 HTTP 請求對映的。同樣地,我們需要將所有的 Method 與其註解建立關聯,HTTP 直接通過註解的值找到對應的 Method,這裡也用 Map 集合儲存。

  • 例項化檢視解析器

初始化工作完成,接下來處理 HTTP 請求,業務流程如下:

  • DispatcherServlet 接收請求,通過對映從 IoC 容器中獲取對應的 Controller 物件;
  • 根據對映獲取 Controller 物件對應的 Method;
  • 呼叫 Method,獲取返回值;
  • 將返回值傳給檢視解析器,返回物理檢視;
  • 完成頁面跳轉。

建立類

思路捋清楚了,接下來開始寫程式碼,我們需要建立以下類。

  • MyDispatcherServlet:模擬 DispatcherServlet
  • MyController:模擬 Controller 註解
  • MyRequestMapping:模擬 RequestMapping 註解
  • MyViewResolver:模擬 ViewResolver 檢視解析器
  • 建立 MyDispatcherServlet,init 方法完成初始化。
    把 Controller 與註解進行關聯
    將 Controller 與註解進行關聯,儲存到 iocContainer 中。哪些 Controller 是需要新增到 iocContainer 中的呢?必須同時滿足兩點:
    • springmvc.xml 中配置掃描的類
    • 類定義處添加了註解
      注意這兩點必須同時滿足。

(1)建立 MyController 註解,作用目標為類:

package morin.springmvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * controller註解 作用目標為類
 * 作用範圍:執行時
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {

    String value() default "";

}

(2)建立 MyRequestMapping 註解,作用目標為類和方法:

package morin.springmvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定義requestMapping註解,作用目標為類和方法,作用範圍為執行時
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
    String value() default "";
}

(3)建立 MyDispatcherServlet,核心控制器,init 完成初始化工作,doPost 處理 HTTP 請求:

package morin.springmvc.servlet;

import morin.springmvc.annotation.MyController;
import morin.springmvc.annotation.MyRequestMapping;
import morin.springmvc.view.MyViewResolver;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * MyDispathcerServlet class
 *
 * @author Molrin
 * @date 2018/11/5 0005
 */
public class MyDispathcerServlet extends HttpServlet {
    /**
     * 模擬IDC容器,儲存bean物件
     */
    private Map<String, Object> iocContainer = new HashMap<String, Object>();
    /**
     * 儲存handler對映
     */
    private Map<String, Method> methods = new HashMap<String, Method>();
    /**
     * 檢視解析器
     */
    private MyViewResolver myViewResolver;

    @Override
    public void init(ServletConfig config) throws ServletException {
        //掃描Controller,建立例項物件,並存入iocContainer
        scanController(config);
        //初始化Handler對映
        initHandlerMapping();
        //載入檢視解析器
        loadViewResolver(config);
    }

    /**
     * 載入檢視解析器
     * @param config 配置
     */
    private void loadViewResolver(ServletConfig config) {
        //建立SAXReader解析xml配置
        SAXReader saxReader = new SAXReader();
        try {
            //解析springMVC.xml
            String path = config.getServletContext().getRealPath("") + "\\WEB-INF\\classes\\" + config.getInitParameter("contextConfigLocation");
            Document document = saxReader.read(path);
            Element root = document.getRootElement();
            Iterator iterator = root.elementIterator();
            while (iterator.hasNext()) {
                Element element = (Element) iterator.next();
                if ("bean".equals(element.getName())) {
                    String className = element.attributeValue("class");
                    Class<?> aClass = Class.forName(className);
                    Object o = aClass.newInstance();
                    //獲取方法物件
                    Method setPrefix = aClass.getMethod("setPrefix", String.class);
                    Method setSuffix = aClass.getMethod("setSuffix", String.class);
                    Iterator beanIter = element.elementIterator();
                    //獲取property值
                    HashMap<String, String> map = new HashMap<String, String>();
                    while (beanIter.hasNext()) {
                        Element next = (Element) beanIter.next();
                        String name = next.attributeValue("name");
                        String value = next.attributeValue("value");
                        map.put(name, value);
                    }
                    for (String key : map.keySet()) {
                        //反射機制呼叫set方法,完成賦值
                        if ("prefix".equals(key)) {
                            setPrefix.invoke(o, map.get(key));
                        }
                        if ("suffix".equals(key)) {
                            setSuffix.invoke(o, map.get(key));
                        }
                    }
                    myViewResolver = (MyViewResolver)o;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化handler對映
     */
    private void initHandlerMapping() {
        Set<String> keySet = iocContainer.keySet();
        for (String value : keySet) {
            //獲取每個類的class物件
            Class<?> aClass = iocContainer.get(value).getClass();
            //獲取該類的方法陣列
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                //判斷方法是否帶有MyRequestMapping註解
                if (method.isAnnotationPresent(MyRequestMapping.class)) {
                    //獲取該註解中的值
                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String ann = annotation.value().substring(1);
                    //將帶有註解的方法存入處理器對映器
                    this.methods.put(ann, method);
                }
            }
        }
    }

    /**
     * 掃描controller
     *
     * @param config 配置
     */
    private void scanController(ServletConfig config) {
        //建立解析xml物件
        SAXReader saxReader = new SAXReader();
        try {
            //獲取xml路徑
            String path = config.getServletContext().getRealPath("") + "\\WEB-INF\\classes\\" + config.getInitParameter("contextConfigLocation");
            //獲取document
            Document document = saxReader.read(path);
            //獲取根元素
            Element rootElement = document.getRootElement();
            //獲取元素迭代器
            Iterator iterator = rootElement.elementIterator();
            //迴圈遍歷
            while (iterator.hasNext()) {
                //獲取單個元素
                Element element = (Element) iterator.next();
                //判斷是否有包掃描標籤
                if ("component-scan".equals(element.getName())) {
                    //獲取掃描的包名
                    String basePackage = element.attributeValue("base-package");
                    //獲取basePackage包下的所有類名
                    List<String> classNames = getClassName(basePackage);
                    for (String className : classNames) {
                        //根據全限定名獲取class物件
                        Class<?> aClass = Class.forName(className);
                        //判斷是否有@Controller註解
                        if (aClass.isAnnotationPresent(MyController.class)) {
                            MyRequestMapping requestMapping = aClass.getAnnotation(MyRequestMapping.class);
                            //獲取該註解中的value
                            String value = requestMapping.value().substring(1);
                            //使用instance方法建立Controller物件並放入iocContainer
                            iocContainer.put(value, aClass.newInstance());
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<String> getClassName(String basePackage) {
        //建立返回值
        List<String> classNames = new ArrayList<String>();
        //將全限定名中的. 全部替換為/
        String newNames = basePackage.replace(".", "/");
        //獲取當前執行緒的類載入器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        URL url = contextClassLoader.getResource(newNames);
        if (url != null) {
            File file = new File(url.getPath());
            //獲取該目錄下的所有檔案路徑的File物件集合
            getFileName(file.listFiles(), basePackage,classNames);
           
        }
        return classNames;
    }
    private void getFileName(File[] file,String basePackage,List<String> classNames){
        String path;
        if (file != null) {
            for (File file1 : file) {
                if (!file1.isFile()) {
                    File[] files = file1.listFiles();
                    path = basePackage+"." +file1.getName();
                    getFileName(files,path,classNames);
                }else {
                    path = basePackage + "." + file1.getName().replace(".class", "");
                    classNames.add(path);
                }
            }
        }
    }
    @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 {
        //獲取請求
        String handlerUri = req.getRequestURI().split("/")[1];
        //獲取controller例項
        Object obj = iocContainer.get(handlerUri);
        String methodUri = req.getRequestURI().split("/")[2];
        //獲取method例項
        Method method = methods.get(methodUri);
        try {
            //反射機制呼叫方法
            String value = (String) method.invoke(obj,req.getParameter("sessionId"));
            //檢視解析器將邏輯檢視轉換成物理檢視
            String view = myViewResolver.jspMapping(value);
            //頁面跳轉
            req.getRequestDispatcher(view).forward(req, resp);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

(4)建立檢視解析器 MyViewResolver:

package morin.springmvc.view;

/**
 * MyViewResolver class
 * 檢視解析器
 * @author Molrin
 * @date 2018/11/5 0005
 */
public class MyViewResolver {

    /**
     * 檢視字首
     */
    private String prefix;
    /**
     * 檢視字尾
     */
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public String jspMapping(String value) {
        return prefix+value+suffix;
    }
}

(5)建立 TestController,處理業務請求:

package morin.springmvc.controller;

import morin.springmvc.annotation.MyController;
import morin.springmvc.annotation.MyRequestMapping;
import org.springframework.web.bind.annotation.CookieValue;

/**
 * TestController class
 *
 * @author Molrin
 * @date 2018/11/5 0005
 */
@MyController
@MyRequestMapping("/testController")
public class TestController {

    @MyRequestMapping(value = "/test")
    public String testHello(){
        System.out.println("執行controller");
        System.out.println("success!");
        return "index";
    }
}

(6)springMVC.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation
            
           

相關推薦

springMVC學習心得springMVC簡單實現

springMVC學習心得及手寫springMVC簡單實現 Spring 是一個企業級開發框架,為解決企業級專案開發過於複雜而建立的,框架的主要優勢之一就是分層架構,允許開發者自主選擇元件。 Spring 的兩大核心機制是 IoC(控制反轉)和 AOP(面向切面程式設計),從開發的角度

java 程式碼簡單模擬SpringMVC

######1.在Spring MVC中,將一個普通的java類標註上Controller註解之後,再將類中的方法使用RequestMapping註解標註,那麼這個普通的java類就夠處理Web請求 ######2.通過一個簡單的java專案來模擬Spring MVC,先說一下整體思

轉載:SpringMVC框架

javaee 作用 小寫 繼承 inf group css finally 減少 帶你手寫一個SpringMVC框架(有助於理解springMVC) 鏈接:https://my.oschina.net/liughDevelop 作者:我叫劉半仙 Spring

跟我一起造輪子 springmvc

targe leg unit nco 容器 ner 並且 dir 有關   作為java程序員,項目中使用到的主流框架多多少少和spring有關聯,在面試的過程難免會問一些spring springmvc spring boot的東西,比如設計模式的使用、 怎麽實現spri

SpringMVC到SpringBoot框架專案實戰

引言 Spring Boot其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。 通過這種方式,springboot是一個快速整合第三方框架的,簡化了xml的配置,專案中再也不包含web.xml檔案了

springmvc流程圖 轉載 自己一個SpringMVC框架

轉載 https://blog.csdn.net/lang_programmer/article/details/71598042 轉載 https://www.cnblogs.com/java1024/p/8556519.html 轉載 https://www.cnblogs.com

spring事務(6)-----SpringMVC模式(@RequestMapping和@Controller註解)

一,spring原生態的程式碼分析 1.1,首先,我們先來認識一下SpringMVC的主要元件   前端控制器(DisatcherServlet):接收請求,響應結果,返回可以是json,String等資料型別,也可以是頁面(Model)。   處理器對映器(HandlerMap

高手過招「效能優化/純SpringMVC框架/MySql優化/微服務」

效能優化那些絕招,一般人我不告訴他 1,支付寶介面的介面如何正確呼叫; 2,從併發程式設計角度來提高系統性能; 3,系統響應的速度縮短N倍的祕密; 4,從Futuretask類原始碼分析到手寫; 5,快速提升Web專案吞吐量;   300行精華程式碼:純手寫SpringMVC框

SpringMVC

相信用過SpringMVC的同學都會對它愛不釋手,它作為MVC框架使用起來簡直就是享受。時間久了相信會問它到底是怎麼實現的呢,今天我們來揭開其神祕的面紗。 這裡我們通過寫一個簡單的例子來模擬SpringMVC的基本原理,希望能夠對愛提問的人有所幫助 1.web.xml中配

學習筆記 利用反射 一個簡單的實體類 轉json 的方法

不得不說 反射真的是個好動  # 貼上我的程式碼   package com.lengff.test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetExce

機器學習實戰之K近鄰改進的約會網站程式碼字型識別程式碼

from numpy import * import operator import os def createDataSet(): group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) labels=['A','A','B','B']

SpringMvc學習心得(三) spring例項化JavaBean的過程

     我們一般是通過呼叫一個類的建構函式完成例項化的工作,spring當然也不例外。然而相比於直接通過建構函式對類進行例項化,spring的例項化過程要複雜得多。    在之前的部落格中,本人曾經說過,任何一個JavaBean都有一個beandefinition。然而b

springmvc原理詳解(springmvc

最近在複習框架 在網上搜了寫資料 和原理 今天總結一下 希望能加深點映像  不足之處請大家指出 我就不畫流程圖了 直接通過程式碼來了解springmvc的執行機制和原理 回想用springmvc用到最多的是什麼?當然是controller和RequestMapping註解啦

SpringMvc學習心得(二)spring註解配置原理淺析

除了使用xml檔案配置以外,spring還支援使用註解實現JavaBean的配置,其具體實現方式網上已經介紹的很詳細,這裡就不再贅述了。本文將主要通過原始碼解析spring註解配置JavaBean的全過程。這裡主要分析的是@component和@Autowired這兩個註解

SpringMVC迷你版

步驟 實現思路: 配置階段 1:在web.xml檔案中配置相應的DispatcherServlet的類路徑 2:指定application.properties的檔案路徑

SpringMVC實戰,從Spring底層原始碼分析與設計

課程內容: 1,三分鐘熟悉Spring底層原始碼,你只需準備好鮮花即可; 2,Spring原始碼很可怕?那是因為你沒聽過James的課; 3,快速熟悉原始碼基礎,洞析SpringMVC與Spring框架關係; 4,@Controller,@Service這些註解算什麼,一

機器學習與資料探勘-logistic迴歸識別例項的實現

本文主要介紹logistic迴歸相關知識點和一個手寫識別的例子實現 一、logistic迴歸介紹: logistic迴歸演算法很簡單,這裡簡單介紹一下: 1、和線性迴歸做一個簡單的對比 下圖就是一個簡單的線性迴歸例項,簡單一點就是一個線性方程表示 (就是用來描述自變數和因

SpringMVC架構,用註解實現springmvc過程(動腦學院Jack老師課後自己練習的體會)

標籤: 1、第一步,首先搭建如下架構,其中,annotation中放置自己編寫的註解,主要包括service controller qualifier RequestMapping 第二步:完成對應的annotation: package com.cn.annotation; import java.

FPGA學習心得(flash讀,+lwip+資料傳送等問題)

前段時間應老闆的專案需求,對硬體絲毫不懂得我開始接觸edk硬體程式設計,感覺這段時間跟硬體打交道,自己都老了不少。首先,硬體程式設計編譯很慢,編譯一次有時候得10-20分鐘,尤其是用verilog寫得程式比較大的時候。其次,除錯非常麻煩,不能像利用c或者c#那樣斷點除錯了,

SpringMVC-實戰篇

       有了手寫SpringMVC的思路,不知道大家是否已經開始實現呢?現在就為大家開始以上一篇文章《手寫SpringMVC-思路篇》的思路來一步步的實現SpringMVC的手寫,讓大家也能在SpringMVC的實現上有個初步的認識。此篇文章可能有些長,希望大家可以一邊