SpringMVC 入門


SpringMVC 框架概述

Spring Web MVC是基於Servlet API構建的傳統Web框架,並且從一開始就已包含在Spring框架中

與Spring Web MVC並行,Spring Framework 5.0引入了一個新的反應式Web框架,其名稱“ Spring WebFlux;

理解:

首先SpringMVC 是一個MVC構架模式的web框架,是基於Servlet的,從Spring第一個版本就一起推出了,

傳統web框架,指的是SpringMVC依然使用多線程同步並發的方式來處理請求,現如今大家都在鼓吹異步並發多么多么好,從測試數據來看異步並發效率的確更好,但是其並不成熟,極大多數公司項目還沒有更新到異步技術,盲目的進行重構可能會引發更多的問題, 並且異步編程在代碼結構上會產生較大的變化,對於初學者而言,掌握難度是較大的;

構架圖:
image-20200206122121806

Spring-MVC在系統中的位置

image-20200206122121806

可以看出

SpringMVC 並沒有代替Servlet,它只是在Servlet上提供了一套封裝好的組件,提高開發效率;

還使得開發出的項目更加規范;否則每個人可能有每個人不同的MVC;

SpringMVC核心組件

思考:

若沒有SpringMVC框架,我們該如何去編寫一個較大的web項目呢,可以發現在選課系統中出現了大量的Servlet,因為一個請求地址就需要一個Servlet,使得項目體積變大,且Servlet是長期存在內存的;

第一步,我們希望用一個Servlet來處理多個請求甚至是所有請求,就需要實現能根據請求路徑查找處理請求方法的邏輯,這也是SpringMVC要做的第一件事情;

  • DispatcherServlet:前端控制器
    用戶請求首先到達前端控制器,它就相當於mvc模式中的c,DispatcherServlet是整個流程控制的調度中心,由它調用其它組件處理用戶的請求,DispatcherServlet的存在降低了組件之間的耦合性。
  • Handler:處理器
    Handler是繼DispatcherServlet前端控制器的后端控制器,DispatcherServlet會將請求發送至對應的Handler來進行處理。Handler是處理業務邏輯的地方,需要我們自己來編寫具體代碼,等同於之前的Service層
  • HandlerMapping:處理器映射器
    HandlerMapping負責根據用戶請求路徑找到Handler,springmvc提供了不同的映射器實現不同的映射方式,例如:BeanName映射,配置文件映射,注解映射等。
  • HandlAdapter:處理器適配器
    通過HandlerAdapter來執行Handler,因為Handler有不同形式,意味着調用方式是不同的,這是適配器模式的應用,我們也可以擴展適配器來實現新的Handler;
  • ViewResolver:視圖解析器
    ViewResolver負責從Handler中獲取數據和視圖,根據邏輯視名稱查找物理視圖文件,並查找View對象,再生成View對象;
  • View:視圖
    View的職責就是裝配數據,SpringMVC框架提供了很多的View視圖類型的支持,包括:jstlView、freemarkerView、pdfView等。常用視圖就是jsp。我們需要根據業務需求,通過頁面標簽或頁面模版技術將模型數據展示給用戶

當然還有一些其他的

類型 說明
HandlerExceptionResolver Handler異常處理器
LocaleResolver 提供國際化的視圖。根據不同地區顯示不同內容
ThemeResolver 根據不同地區提供個性化的布局
MultipartResolver 解析multipart請求數據,如瀏覽器表單文件上傳
FlashMapManager 常用於通過重定向將屬性從一個請求傳遞到另一個請求

請求處理流程簡述

入門程序

pom依賴

<?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>org.example</groupId>
  <artifactId>MVC01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>MVC01 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <!--   SpringMVC -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>

    <!--JEE相關的-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

web.xml配置

與在web項目中使用Spring中相同的是,我們也需要讓SpringMVC隨着web項目啟動,SpringMVC的做法是利用DIspatcherServlet;

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--        初始化參數指定配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup></load-on-startup>
    </servlet>

<!--    要交給SpringMVC處理的請求-->
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

DispatcherServlet做了兩個事情,1:初始化一個Spring容器,2:注冊一個Servlet

SpringMVC本質上也是一個Spring容器,在不涉及WEB時使用方法和Spring沒有任何區別;ß

url-pattern

只有請求地址能夠匹配到到被DispatcherServlet的url-pattern的請求才會被SpringMVC處理,那么那些請求要交給SpringMVC處理呢,通常是除靜態資源以外的請求;

常用pattern:

pattern 說明
/ 除了.jsp 以外的所有請求
/* 所有請求
*.action 所有 以action結尾的請求

需要說明的是action並不是固定的不同公司可能不同,但無論是點什么,其目的都是為了和靜態資源加以區分

創建控制器類

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView();//創建一個表示模型和視圖的對象
        modelAndView.setViewName("index.jsp");//設置視圖名稱
        modelAndView.addObject("msg","hello springMVC");//添加視圖需要的數據
        //httpServletRequest.setAttribute("msg","hello SpringMVC"); //等同於上面一行
        return modelAndView; //返回模型和視圖給dispatcherServlet
    }
}

ModelAndView,其實就是把視圖資源和數據打包到一起,然后視圖名稱交給視圖解析器,Object放到請求中;

注冊控制器到容器中

springmvc.xml

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

    <bean name="/testController" class="com.yh.controller.TestController"/>
</beans>

DispatcherServlet是按照請求路徑來查找Handler,我們必須告訴SpringMVC,這個控制器是用來處理哪個請求地址的,默認情況下,SpringMVC會查找beanName與請求地址相同的Handler來處理;

測試:

打開瀏覽器輸入地址:http://localhost:8080/MVC01_war_exploded/testController

image-20200206143953076

我們在Request中添加了key為msg的字符內容hello SpringMVC

看到上述內容說明SpringMVC以及成功處理了請求;

注解配置Controller

上述方法每個Controller只能處理一個請求地址,不夠靈活,且需要Controller實現指定接口,這是就需要使用注解來配置Controller了;

  • @Controller

    該注解寫在類上,用於注冊控制器bean到容器中,這是之前以及學習過的

  • @RequestMapping("url")

    該注解寫在方法上時,用於為方法指定要匹配的url,該url是相對根路徑的

    寫在類上時類上的url是相對於根路徑,而類中方法則相對於類的url

案列:

@Controller
//@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getMsg")
    public ModelAndView getMsg(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("/index.jsp");
        modelAndView.addObject("msg","hello SpringMVC annotation!");
        return modelAndView;
    }
}

請求地址:http://localhost:8080/MVC01_war_exploded/getMsg,可正常訪問

當把類上注釋的url打開時,上面的地址404了

正確地址:http://localhost:8080/MVC01_war_exploded/user/getMsg

需要注意的是:
viewName路徑若不帶/時則從當前請求的位置查找文件,帶/則表示從根路徑查找

默認配置閱覽

我們完成了一個簡單的入門案例,但是你會發現除了DispatcherServlet之外沒有出現其他上面提到過的組件,那么它們是不是沒有作用呢?

其實SpringMVC提供了很多默認配置,使我們可以快速的開項目,而無需繁瑣的配置,在SpringMVC的包下可以找到一個DispatcherServlet.properties配置,這便是默認的配置文件了;

RouterFunctionMapping和HandlerFunctionAdapter都是webFlux中的這里不多關注;

HandlerMapping 映射方式
BeanNameUrlHandlerMapping 用beanName作為url
RequestMappingHandlerMapping 使用注解配置url
HandlerAdapter 處理對象:
HttpRequestHandlerAdapter 實現HttpRequestHandler的處理器
SimpleControllerHandlerAdapter 實現Controller的處理器
RequestMappingHandlerAdapter 使用注解的handler,無需實現接口
SimpleServletHandlerAdapter Servlet類Handler,需繼承HttpServlet

SimpleServletHandlerAdapter默認是沒有的,當需要使用Servlet相關API時使用,需要在配置文件中聲明

<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>

注意:當手動添加了適配器后,系統就不會自動添加任何其他適配器了;

SimpleServletHandlerAdapter案例:

@Controller("/servletController")
public class YouController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("servlet API");
    }
}

請求地址:http://localhost:8080/MVC01_war_exploded/servletController,記得注冊Adapter到容器中;

在上述4中handler中最常用的是使用注解形式的;

視圖解析配置

視圖解析器用於查找視圖文件,及生成視圖對象,我們不用過多關注,唯一會用到的就是,為視圖名稱配置前綴和后綴從而簡化,Handler中的書寫

在一個實際項目中頁面文件可能比較多,可以用文件夾管理,但是這導致我們在編寫視圖名稱時更加繁瑣,例如:

handler中:

這時就可在配置中對視圖解析器進行相關設置;

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <!--指定視圖類型-->
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
  <!--指定前綴-->
    <property name="prefix" value="/pages/jsp/"/>
  <!--指定后綴-->
    <property name="suffix" value=".jsp"/>
</bean>

處理器中:

@RequestMapping("/getMsg")
public ModelAndView getMsg(){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("index");
    modelAndView.addObject("msg","hello SpringMVC annotation!");
    return modelAndView;
}

視圖解析器會自動在viewName前后分別拼接前綴和后綴;如:/pages/jsp/index.jsp

啟用MVC 配置

上面提到,SpringMVC默認會加載DispatcherServlet.properties中的配置作為默認配置,當我們需要添加額外的自定義配置時該怎么辦呢?這是我們需要啟用MVC配置,通過在配置文件中添加以下標簽

<mvc:annotation-driven/>

為了減少配置項,該標簽向容器中添加了提供MVC基礎服務的Bean,並添加了json,xml,的轉換器

有興趣可以源碼位置:web包下的AnnotationDrivenBeanDefinitionParser,

此時看不出該標簽對系統有什么影響,但在后續自定義驗證器,轉換器時就不得不使用到該標簽了

官方原話:

"in XML configuration, you can use the `<mvc:annotation-driven/>` element to enable MVC configuration, the preceding example registers a number of Spring MVC infrastructure beans and adapts to dependencies available on the classpath (for example, payload converters for JSON, XML, and others)"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM