<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Sping MVC 正式的名字為 Spring Web MVC,是 Spring Framework 框架中的其中一個模組,基於 Servlet API 構建,同時使用 MVC 的架構模式,主要用以簡化傳統的 Servlet + JSP 進行 web 開發的工作。
Spring MVC 基於 MVC 模式,因此理解 Spring MVC 需要先對 MVC 模式有所瞭解。
MVC 即 Model-View-Controller 是軟體開發中一種常用的架構模式,將軟體系統分為三層:模型(Model)、檢視(View)、控制器(Controller),各部分根據職責進行分離,使程式的結構更為直觀,增加了程式的可延伸性、可維護性、可複用性。可以用如下的圖形來表示三者之間的關係。
上世紀 90 年代,隨著網際網路的發展,基於瀏覽器的 B/S 模式隨之流行,最初瀏覽器向伺服器請求的都是一些靜態的資源,如 HTML,CSS 等,為了支援根據使用者的請求動態的獲取資源,Java 提出了 Servlet 規範。
此時 Servlet 可以說是一個大雜燴,瀏覽器接收的 HTML 都是通過 Servelt 一行一行的輸出,比較繁瑣,並且寫後端程式碼的程式設計師還要熟悉前端技術,為了解決這個問題,sun 公司又借鑑 ASP 提出了 JSP。
JSP 和 HTML 相似,只是在 JSP 檔案中可以嵌入 Java 程式碼,減少了直接使用 Servlet 產生的大量冗餘程式碼。此時 JSP 同時充當模型、檢視、控制器的角色,為了解決前後端程式碼仍然揉在一起的問題,Java Web MVC 模式後來被提出,JavaBean 充當模型、JSP 充當檢視,Servlet 充當控制器,流程如下圖所示。
瀏覽器的請求先經過 Servlet,Servlet 控制整個流程,使用 JavaBean 查詢並儲存資料,然後攜帶 JavaBean 中的資料到 JSP 頁面中,這個就是 Java 中早期的 Web MVC 架構模式了。
Spring MVC 架構模式對 Java Web 中的 MVC 架構模式加以擴充套件,將控制器拆分為前端控制器 DispatcherServlet 和後端控制器 Controller,將 Model 拆分成業務層(Service) 和資料存取層(Respository),並且支援不同的檢視,如 JSP、FreeMarker 等,設計更為靈活,請求處理流程如下。
瀏覽器的請求先經過 DispatcherServlet,DispatcherServlet 負責分發請求,因此 DispatcherServlet 又被稱為前端控制器。DispatcherServlet 其後的 Controller 又被稱為後端控制器,Controller 可以選擇性的呼叫 Service、Repository 實現業務邏輯,DispatcherServlet 拿到 Controller 提供的模型和檢視後,進行渲染並返回給瀏覽器。當然了,這裡只是為了方便理解 Spring MVC 描述的大概流程,具體流程會在後文介紹。
雖然現在 SpringBoot 已經成為主流,但是我仍然想從單純的 Spring MVC 講起,因為 SpringBoot 也只是在 Spring Framework 其上新增了一些自動化的設定,這些自動化的設定會讓我們忽略背後的技術原理。
幾年的 Spring 的教學中都會提出使用 Spring MVC 首先需要去 Spring 官網下載一大堆的依賴,而現在有了 maven 之後再也不必關係這些亂七八糟的依賴及其依賴關係。如果你不瞭解 maven,建議先去了解 maven 後再回頭看下面的內容。
新建 maven 專案,並引入 Spring MVC 的依賴,注意這裡引入的版本號是 5.2.6,Spring Framework 5 開始對 JDK 版本的要求是 1.8 及以上。完整的 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>com.zzuhkp</groupId> <artifactId>mvc-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>mvc-demo</finalName> </build> </project>
傳統的 Java Web 專案使用 Servlet 處理請求,Spring MVC 遵循了 Servlet 規範,提供了一個名稱為 DispatcherServlet 的 Servlet 類,使用 Spring MVC 需要先宣告這個 Servlet。
DispatcherServlet 整合了 IOC 容器,所有處理 Web 請求的元件都存至 IOC 容器中,然後使用這些 bean 處理控制整個請求過程。
有兩種宣告 DispatcherServlet 的方式,第一種方式是直接在類路徑下的/WEB-INF/web.xml檔案中設定。
<?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>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
第二種方式基於 Servlet 3.0 提出的 ServletContainerInitializer 介面,Servlet 容器會從類路徑中查詢實現了這個介面的類,並在啟動時回撥這個介面中的方法,Spring MVC 已經將這個介面實現為 SpringServletContainerInitializer,在其內部呼叫了 WebApplicationInitializer 介面完成初始化,因此實現 WebApplicationInitializer 介面再新增 DispatcherServlet 也可以,和上述 xml 等效的 java 程式碼如下。
public class MvcXmlInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { XmlWebApplicationContext context = new XmlWebApplicationContext(); DispatcherServlet dispatcher = new DispatcherServlet(context); Dynamic dynamic = servletContext.addServlet("dispatcher", dispatcher); dynamic.addMapping("/"); dynamic.setLoadOnStartup(1); } }
除了上述使用者自定義的 WebApplicationInitializer,Spring 還自定義了一個支援註解設定的抽象實現類 AbstractAnnotationConfigDispatcherServletInitializer,這個類會自動向 Servlet 上下文中註冊 DispatcherServlet,實現這個類然後指定設定類即可。
public class MvcAnnotationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{MvcConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
這裡我們使用 web.xml 設定進行演示,我們設定的 DispatcherServlet 宣告的對映路徑是/
,因此,所有的請求都會到達 DispatcherServlet,然後再分派給不同的處理器處理。
Spring MVC 使用 IOC 容器儲存處理請求的元件,包括處理器在內的所有自定義的與 Web 請求有關的元件都需要新增到 Spring 的設定中。
DispatcherServlet 初始化時預設使用的容器是 XmlWebApplicationContext,雖然 Spring 預留了擴充套件點用於修改容器型別,非必要情況下還是建議不要修改,這個容器預設情況下會使用類路徑下/WEB-INF/{servlet-name}-servlet.xml
檔案作為容器的組態檔,我們宣告的 DispatcherServlet 名為 dispatcher,因此我們建立/WEB-INF/dispatcher-servlet.xml
檔案作為容器的設定。另外還可以使用 Servlet 的初始化引數 configLocation 指定 Spring 容器組態檔路徑。
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
Spring 組態檔內容如下。
<?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 id="/hellohandler" class="com.zzuhkp.mvc.handler.HelloSpringMVCHttpRequestHandler"/> </beans>
這裡宣告了一個型別為 HelloSpringMVCHttpRequestHandler 的 bean,其 id 為請求路徑/hellohandler
,這個類的定義如下。
public class HelloSpringMVCHttpRequestHandler implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("Hello,HelloSpringMVCHttpRequestHandler"); } }
這樣設定的目的是希望當/hellohandler
請求到達時,能夠使用我們提供的 HelloSpringMVCHttpRequestHandler 處理請求。
到了這裡,將專案發布到 Tomcat,我這裡使用的 Tomcat 版本號是 9.0.54,可以看到效果如下。
那為什麼將處理器的 bean id 設定為請求路徑就可以使用這個處理器進行處理呢?Spring MVC 為了靈活的查詢處理器內部使用了 HandlerMapping 將請求對映到處理器,Spring 預設情況下會使用BeanNameUrlHandlerMapping
對映請求,這個對映器將請求路徑作為 id 查詢處理器。除了預設情況下使用的這個對映器,我們還可以設定 SimpleUrlHandlerMapping 對映器,和上述等效的 Spring 設定如下。
<?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 id="hellohandler" class="com.zzuhkp.mvc.handler.HelloSpringMVCHttpRequestHandler"/> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <map> <entry key="/hellohandler" value-ref="hellohandler"/> </map> </property> </bean> </beans>
看到這裡,細心的小夥伴可能會有疑問,說好的 DispatcherServlet 將請求分派給 Controller 呢?這裡暫時不用著急,Controller 其實是 Spring MVC 的處理器型別之一,這裡的 HttpRequestHandler 同樣是 Spring MVC 的處理器。
Spring 對多種處理器進行了支援,具體則是使用 HandlerAdapter 對處理器進行適配,Spring MVC 內部已經預設了一些介面卡,HttpRequestHandler 的介面卡是 HttpRequestHandlerAdapter,Controller 的介面卡 SimpleControllerHandlerAdapter 也是 Spring MVC 預設支援的。
預設的 HandlerAdapter 已經足夠支援日常所需,一般不會自定義 HandlerAdapter。
下面嘗試使用 Controller 作為處理器處理請求,定義實現 Controller 介面的 HelloSpringMVCController 類如下。
public class HelloSpringMVCController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("/WEB-INF/view/welcome.jsp"); modelAndView.addObject("hello", "HelloSpringMVCController"); return modelAndView; } }
然後在 Spring 組態檔中新增這個類作為 bean。
<bean id="/hellocontroller" class="com.zzuhkp.mvc.handler.HelloSpringMVCController"/>
到了這裡,終於可以看到 Controller 了,Controller 處理請求,返回了一個型別為 ModelAndView 的物件。
ModelAndView 包含模型和檢視,這裡向模型中新增了屬性 hello,並且指定了/WEB-INF/view/welcome.jsp
檔案作為檢視名,這個檔案內容如下。
<html> <body> <h2>Hello,${requestScope.hello}</h2> </body> </html>
啟動 Tomcat 存取 /hellocontroller
效果如下。
成功將模型中的資料展示到檢視。
為了支援不同的檢視,如 JSP、FreeMarker 等,ModelAndView 中的檢視名稱被設計成虛擬的,具體的檢視由檢視解析器 ViewResolver 進行解析,預設情況下使用的檢視解析器是 InternalResourceViewResolver ,這個檢視解析器基於 URL 解析檢視。同時也可以嚮應用上下文中設定自己的檢視解析器。新增自定義的 InternalResourceViewResolver 到 Spring 組態檔。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view"/> <property name="suffix" value=".jsp"/> </bean>
然後設定檢視名時就可以忽略路徑字首/WEB-INF/view
和字尾.jsp
,設定字首字尾後上述範例中 HelloSpringMVCController 就可以將檢視名從 /WEB-INF/view/welcome.jsp
簡化為 welcome
。
上述範例中使用了不少 DispatcherServlet 使用的元件,Spring MVC 預設情況下已經提供了一些,如果需要自定義,則將自定義的元件新增到上下文設定中介面,十分方便,那麼 Spring 預設情況下使用了哪些元件處理請求呢?
spring-webmvc 模組類路徑下 org/springframework/web/servlet/DispatcherServlet.properties
檔案定義了這些預設的設定,具體如下。
基於組態檔的 Spring Web MVC 專案在前些年確實比較流行,然而現在註解已經成為 Spring 開發的主流。下面通過純註解的方式對上面的範例進行改造。
pom 檔案不需要進行變化,首先要提供 Spring 設定類。
@ComponentScan("com.zzuhkp.mvc") public class MvcConfig { }
這裡只新增了元件掃描能力,Spring 會將給定包下標註了 @Component 的類作為 bean 進行處理。然後將將這個類設定為設定類即可,這裡可以參見使用上述提供的 DispatcherServlet 第二種宣告方式。
然後提供基於註解的控制器。
@Controller public class HelloSpringMVCAnnotationController { @GetMapping("/helloAnnotationController") public String helloMVC(@RequestParam("hello") String hello, Model model) { model.addAttribute("hello", hello); return "/WEB-INF/view/welcome.jsp"; } }
基於註解的控制器不需要實現特定的介面,直接在類上新增 @Controller 註解即可,這裡定義了一個處理 /helloAnnotationController
路徑 GET 請求方式的方法,並且接收 hello 引數,存放至 model 中,然後返回了檢視名。這裡直接複用了上面範例中的檢視。最終效果如下。
基於註解的控制器是 Spring MVC 中設計最為靈活的地方,這裡可以先考慮下,Spring 是怎麼適配使用者自定義的控制器的?控制器方法中的引數如何賦值呢?如何將控制器方法的返回值解析為檢視?Spring 如何支援 RESTFUL 風格的介面的?後面會寫幾篇文章繼續分析。
DispatcherServlet 請求處理流程已經穿插在前面的範例中介紹,直接看前面的描述可能不是很直觀,這裡總結了一張圖來梳理整個流程。
整個流程串聯起來如下。
本文先介紹 MVC 架構模式,然後通過範例的方式對 Spring MVC 的使用方式及執行流程進行介紹,最後還使用一個流程圖總結。
Spring MVC 中所有的擴充套件都基於 DispatcherServlet 處理請求的這個流程,可以說理解了這個流程圖就理解了 Spring MVC 的原理,後面將會對這個流程進行細化,繼續介紹 Spring MVC 的其他內容。
到此這篇關於理解Spring MVC的文章就介紹到這了,更多相關理解Spring MVC內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45