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

可以看出
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
我們在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)"