Spring MVC知識梳理



同上一篇博客,複習梳理SpringMVC知識點,此次的梳理比較快,不少細節沒有顧慮到,後期可能會回來補充html


1. 總體架構


1.1 在學習了SSM框架後咱們來理清三者的應用層面


瀏覽器發送請求,請求到達SpringMVC處理,而後調用業務層邏輯實現,跟着持久層操做獲取數據,最後逆序響應到瀏覽器。前面咱們複習了Mybaits和Spring框架,咱們固然不陌生了,如今就來了解下SpringMVC到底有什麼做用前端




1.2 MVC

MVC模型中,M是把瀏覽器傳的參數封裝成的pojo類型,V則表明視圖,C就是控制器也是重點。SpringMVC是以組件的形式來造成總體的,下面也是畫圖來解釋java



網圖,侵刪mysql


  • 核心控制器被Tomcat初始化並主動加載applicationContext配置文件
  • 用戶發送請求
  • 請求到達核心控制器
  • 核心控制器交由映射器處理映射地址
  • 核心控制器找到適配器來適配處理器(適配器模式)
  • 將請求過來的數據進行轉換
  • 將轉好的數據給處理器處理並沿路返回
  • 最後經過視圖解析器解析
  • 響應對應的頁面


從上面能夠看出 DispatcherServlet 是核心指揮中心,MVC框架圍繞其來設計的,處理全部的http請求和響應web


DispatcherServlet 收到請求後根據HandlerMappering來選擇而且調用映射的控制器spring


控制器接收到請求後基於GET、POST調用適當的Servce方法後將數據返回到DispatcherServlet中sql


上面所說的HandlerMapping、Controller是WebApplicationContext的一部分,其是ApplicationContext的擴展,也是BeanFactory的擴展apache


啓動Tomcat,初始化web.xml中的 DispatcherServlet ,而DispatcherServlet 框架則嘗試加載applicationContext.xml配置文件內容json












2. 映射關係

MVC做用在表現層用來處理請求,因此地址映射也在這裏,即在Controller中,請求是在方法上處理的,不是類上(這也是單例的緣由,類上使用映射即爲分模塊做用),方法的返回值默認爲返回的網頁地址(如今先後端分離使用得比較少了,下面講解都是用先後端分離模式),其映射關係使用註解的過程爲:後端


@Controller
@RequestMapping("/user")
public class HelloController {

    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    public String sayHello(){
        System.out.println("Hello World");
        return "success";
    }
}












3. 參數綁定

這裏是重點,由於請求通常都帶數據的,而後在這裏綁定成Model,方便咱們使用,不用再像JavaWeb程序中request.getParameter()了,支持基本類型、String類型,bean類型以及集合類型


這裏一個小插曲,若是要獲取request、response,則在方法參數上本身添加便可


3.0 這裏先給出須要用到的Bean

public class User {

    private int id;
    private String name;
    private String email;
    private InnerBean innerBean;
    private List<InnerBean> innerBeanList;
    private String[] array;

    // 省略各類getter / setter
}
public class InnerBean {

    private String inner;

    public void setInner(String inner) {
        this.inner = inner;
    }
}


3.1 簡單參數綁定

MVC框架會在方法參數中綁定請求中名字相同的變量(使用了反射),簡單參數爲基本類型和String,參數名若不一樣則使用@RequestParam註解綁定

<form action="param/param1" method="get">
    id:<input type="text" name="id">
    name:<input type="text" name="name">
    email:<input type="text" name="email">
    <input type="submit" value="submit1">
</form>

// 表單中name和形參名字相同

// 簡單參數
@RequestMapping(value = "/param1")
public String getParameter1(String id, String name, String email) {
    System.out.println(id + name + email) ;
    return "success";
}


3.2 Bean封裝

bean類型封裝是用過裏面的setter實現的,並且還有bean中有bean的狀況

<form action="param/param2" method="get">
    id:<input type="text" name="id">
    name:<input type="text" name="name">
    email:<input type="text" name="email">
    inner:<input type="text" name="innerBean.inner">
    <input type="submit" value="submit2">
</form>


// 封裝Bean對象,依靠setter方法
@RequestMapping(value = "/param2")
public String getParameter2(User user) {
    System.out.println(user) ;
    return "success";
}


3.3 集合封裝

<form action="param/param3" method="get">
    id:<input type="text" name="id">
    name:<input type="text" name="name">
    email:<input type="text" name="email">
    inner:<input type="text" name="innerBeanList[0].inner">
    inner:<input type="text" name="innerBeanList[1].inner">
    inner:<input type="text" name="array[0]">
    inner:<input type="text" name="array[1]">
    <input type="submit" value="submit2">
</form>



// 封裝集合
@RequestMapping(value = "/param3")
public String getParameter3(User user) {
    System.out.println(user) ;
    return "success";
}












4. 類型轉換器

請求傳過來的數據都是字符串,那麼咱們使用的時候爲何能夠獲取其餘類型呢?這裏是使用了框架內部的默認轉換器因此才能夠取得其餘類型數據,但若是默認轉換器識別不了,那麼咱們就要本身配置類型轉換器來實現功能


這裏有個場景:前端傳2020/3/2過來讓Date類型接收是沒問題的,可是若是前端傳了2020-3-2呢?這樣就會報錯,由於2020-3-2框架沒有這個轉換器來轉成Date類型,那麼就須要咱們手動來設置


@Controller
@RequestMapping(value = "/converter")
public class ConverterController {

    @RequestMapping(value = "/converter")
    public String converterMethod(Date date){
        System.out.println(date);
        return "success";
    }
}


4.1 建立轉換器類

這個類實現了Converter<S,T>接口,這個泛型要本身添加,返回類型爲轉換好的類型

public class StringToDateConverter implements Converter<String, Date> {

    /**
     * 須要本身手動添加泛型,s指傳進來的字符串
     */
    public Date convert(String s) {
        s = s.replace('/','-');
        DateFormat sdf = new SimpleDateFormat("yyyy-MM-DD");
        try {
            return sdf.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}


4.2 將自定義轉換器註冊在轉換器服務工廠中,並給容器管理

<!--  自定義類型轉換器  -->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <!--  set標籤  -->
        <set>
            <bean class="com.howl.util.StringToDateConverter"></bean>
        </set>
    </property>
</bean>


4.3 註冊組件

由於MVC是基於組件的,因此使用了組件就要在配置文件中註冊

<!--  開啓mvc註解支持,而且組件生效,默認使用適配器和映射器  -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>












5. 文件上傳

要求:

一、表單要是enctype="multipart/form-data"

二、方法要是POST

三、輸入框要是<input type="file">


當上傳表單爲多個文件時,根據hppt請求體來分割很複雜,因此要藉助第三方jar,也就是傳統的上傳方法,該方法依賴 commons-fileupload(固然下面的MVC的簡化文件上傳也要該依賴),傳統的文件上傳筆者已經寫過一篇博文了,請點擊這裏


至於MVC的上傳呢,更加簡便。MVC提供了MultipartFiled對象,須要表單的name與之對應

<p>文件上傳</p>
<form action="upload/upload" method="post" enctype="multipart/form-data">
    選擇文件:<input type="file" name="uploads">
    選擇文件:<input type="file" name="uploads">
    <input type="submit" value="submit">
</form>





@RestController
@RequestMapping(value = "/upload")
public class FileUploadController {

    @RequestMapping(value = "/upload")
    public String fileUploadMethod(HttpServletRequest httpServletRequest, MultipartFile[] uploads) throws IOException {

        // 建立目錄
        String path = httpServletRequest.getSession().getServletContext().getRealPath("/uploads/");
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }

        for(MultipartFile value : uploads){

            // 原名
            String originalFilename = value.getOriginalFilename();
            // 生成隨機名
            String fileName = UUID.randomUUID().toString().replace("-","") + "_" + originalFilename;
            // 和傳統文件上傳不一樣,參數爲File,差異看博客
            value.transferTo(new File(path,fileName));
        }

        // 筆者這裏返回了地址,通常返回成功消息的
        return path;
    }
}


MVC是基於組件的,因此文件解析器也是一個組件須要配置


<!--  文件解析器,名字必須是multipartResolver  -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"></property>
    <property name="maxUploadSize" value="1024102410"></property>
</bean>












6. 異常處理


正常操做是底層發生異常會一直向上拋,直到發給瀏覽器用戶看到,咱們要避免這種事情發生,就須要異常處理,因此咱們要把流程改爲下面這樣



6.1 編寫自定義異常類(作提示信息)

public class MyException extends Exception {

    private String msg;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public MyException(String msg) {
        this.msg = msg;
    }
}




6.2 編寫全局異常處理器(註解版)

// 獲取全部異常
@RestControllerAdvice()
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public String handleException(Exception e){

        // 若是是自定義異常,則發送本身的消息
        if(e instanceof MyException){
            return ResponseHelper.error(((MyException) e).getMsg(),new User());
        }

        // 不然發送系統錯誤
        return ResponseHelper.error("系統錯誤");
    }
}












7. 攔截器

相似於Filter,但攔截器是對處理器Controller進行預處理和後處理,不一樣於Filter攔截Servlet。攔截器是MVC內部的,使用MVC框架纔有攔截器,而過濾器是javaWeb內部的。範圍不一樣,Filter中配置 /*會過濾全部請求,攔截器應用場景有:權限檢查和日誌處理


7.1 實現HandlerInterceptor

這個接口要本身手動輸入重寫,由於1.8以後接口變了,筆者在這裏配置權限檢查


public class MyInterceptor implements HandlerInterceptor {

    /**
     * @return true表示放行,執行下一個攔截器,false表示攔截
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        Object user = request.getSession().getAttribute("user");
        if(user == null){
            // 沒登陸,重定向
            response.sendRedirect("/admin/login.html");
        }
        // 不然放行
        return  true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}




7.2 applicationContext配置攔截器

萬物皆組件


<!--  配置攔截器,注意path是寫url地址  -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/interceptor/*"/>
        <bean class="com.howl.interceptor.MyInterceptor"></bean>
    </mvc:interceptor>
    
    <!--  第二個攔截器應該這裏配  -->
    <mvc:interceptor>
        <mvc:mapping path="/interceptor/*"/>
        <bean class="com.howl.interceptor.MyInterceptor"></bean>
    </mvc:interceptor>

</mvc:interceptors>












8. 註解總覽

@Controller
@RequestMapping(value = "/hello",method = RequestMethod.GET)
@RequestParam(value = "name") // 用於匹配名字不一致
@PathVariable(value = "sid")  // 綁定url中的佔位符,主要用於Restful風格,下面有這裏

@ResponseBody // 主要用於響應json數據,即Controller方法的返回值經過適當轉換器後,寫入Response不走視圖解析器,筆者用fastjson將bean轉換成json即String類型返給前端,即先後端分離

@RequestBody  // 若異步請求,則發送給後端的是json數據沒法綁定參數,用了這個註解,將獲取請求體中所有參數,以key=value的形式,get方法不在請求體中,沒法使用,當以鍵值對出現時,則是換成普通請求的數據格式,使用setter將綁定參數

@@RestController // @Controller和@ResponseBody的結合,用於先後分離,不走視圖解析器,可放於類上,則類中的所有方法適用,而@RequestBody則不行


@PathVariable

@RequestMapping("/anno2/{sid}")
public String annoMethod2(@PathVariable(value = "sid") int id) {
    System.out.println(id);
    return "success";
}












9. 補充


@RestController中文亂碼

其默認使用tomcat的編碼,並且直接返回給前端因此會亂碼


方法一:映射註解上加屬性
@RequestMapping(value = "/user", produces = "application/json;charset=utf-8")


方法二:全局配置編碼問題

<!--  開啓mvc的註解支持,而且在Responsebody上使用UFT-8  -->
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>




DispatcherServlet在配置映射關係中用/,則會攔截全部請求,包括靜態資源而致使沒法訪問,因此要在applicationContext中配置不攔截

<!--  配置靜態資源不攔截  -->
<mvc:resources mapping="/pages/*" location="/pages/"></mvc:resources>


MVC 三大組件:適配器,映射器,解析器


在Spring的基礎上須要的額外jar包:spring-web、spring-mvc


約束

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

</beans>


pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.howl</groupId>
    <artifactId>SpringPractice</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.1</version>
        </dependency>
        
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        
        <!--  HttpServletRequest要用,通常Tomcat自帶  -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>
</project>
相關文章
相關標籤/搜索