SpringMvc demo示例及原始碼詳細分析
三層架構介紹
我們的開發架構一般都是基於兩種形式,一種C/S架構,也就是客戶端/伺服器,另一種是B/S架構,也就是瀏覽器/伺服器。在JavaEE開發中,幾乎全部都是基於B/S架構的開發。那麼在B/S架構中,系統標準的三層架構包括:表現層、業務層、持久層。三層架構在我們的實際開發中使用的非常多。
三層職責
表現層
也就是我們長說的web層。它負責接收客戶端請求,向客戶端響應結果,通常客戶端使用http協議請求web層,web需要接收http請求,完成http響應。
表現層包括展示層和控制層:控制層負責接收請求,展示層負責結果的展示。
表現層依賴業務層,接收到客戶端請求一般會呼叫業務層進行業務處理,並將處理結果響應給客戶端。
表現層的設計一般都是使用mvc模型。(mvc是表現層的設計模型,和其他層沒有關係)
業務層
也就是我們常說的 service層。它負責業務邏輯處理,和我們開發專案的需求息息相關。web層依賴業務層,但是業務層不依賴web層。
業務層在業務處理時可能會依賴持久層,如果要對資料持久化需要保證事務一致性。(也就是我們說的,事務應該放到業務層來控制)
持久層
也就是我們常說的dao層。負責資料持久化,包括資料層即資料庫和資料訪問層,資料庫是對資料進行持久化的載體,資料訪問層是業務層和持久層互動的介面,業務層需要通過資料訪問層將資料持久化到資料庫中。
通俗的講,持久層就是和資料互動,對資料庫表進行增刪改查的。
mvc設計模式介紹
mvc全名是Model View Controller,模型(Model)-檢視(View)-控制器(Controller)的縮寫,是一種用於設計建立web應用程式表現層的模式。mvc中每個部分各司其職:
Model(模型)
模型包含業務模型和資料模型,資料模型用於封裝資料,業務模型用於處理業務。
View(檢視)
通常指的就是我們的jsp或者html。作用一般就是展示資料的。
通過檢視是依據模型資料建立的。
Controller(控制器)
是應用程式中處理使用者互動的部分。作用一般就是處理程式邏輯的。
SpringMVC介紹
Spring MVC是什麼?
SpringMVC是一種基於Java的實現MVC設計模型的請求驅動型別的輕量級Web框架,屬於SpringFrameWork的後續產品,已經融合在Spring Web Flow裡面。Spring框架提供了構建Web應用程式的全功能MVC模組。使用Spring可插入的MVC架構,從而在使用Spring進行Web開發時,可以選擇使用Spring的Spring MVC框架或整合其他MVC開發框架,如Struts1(現在一般不用),Struts2等。
SpringMVC已經成為目前最主流的MVC框架之一,並隨著Spring3.0的釋出,全面超越Struts2,成為最優秀的MVC框架。
它通過一套註解,讓一個簡單的Java類稱為處理請求的控制器,而無需實現任何介面。同時它還支援RESTful程式設計風格的請求。
總結
Spring MVC和Struts2一樣,都是為了解決表現層問題的web框架,他們都是基於MCC設計模式的。而這些表現層框架的主要職責就是處理前端HTTP請求。
Spring MVC由來?
Spring MVC全名叫Spring Web MVC,它是Spring家族Web模組的一個重要成員。這一點,我們可以從Spring的整體結構中看的出來:
為什麼學習SpringMVC?
也許你會問,為什麼要學習Spring MVC呢?struts2不才是主流嘛?看SSH的概念有多火?
其實很多初學者混淆了一個概念,SSH實際上指的是Struts1.x+Spring+Hibernate。這個概念已經有十幾年的歷史了。在Struts1.x時代,它是當之無愧的霸主,但是在新的MVC框架湧現的時代,形式已經不是這樣了,Struts2.x藉助了Struts1.x的好名聲,讓國內開發人員認為Struts2.x是霸主繼任者(其實兩者在技術上無任何關係),導致國內程式設計師大多數學習基於Struts2.x的框架,又一個貌似很多的概念出來了S2SH(Struts2+Spring+Hibernate)整合開發。
SpringMVC如何處理請求?
SpringMVC是基於MVC設計模型的,MVC模式指的就是Model(業務模型)、View(檢視)、Controller(控制器)。SpringMVC處理請求就是通過MVC這三個角色來實現的。
注:不要把MVC設計模式和工程的三層架構混淆,三層結構指的是表現層、業務層、資料持久層。而MVC只針對表現層進行設計。
下面讓我們看看處理流程吧
第一個MVC程式
達到效果
- 學會如果配置前端控制器
- 如何開發處理器
任務需求
訪問/queryItem,返回商品列表頁面,商品資料暫時使用靜態資料(不從資料庫查詢並返回)。
實現
pom.xml
<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.cyb</groupId> <artifactId>springmvc-demo01</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <!-- spring ioc元件需要的依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.1.RELEASE</version> </dependency> <!-- 基於AspectJ的aop依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- spring MVC依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.1.RELEASE</version> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!-- 配置Maven的JDK編譯級別 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> </configuration> </plugin> <!-- tomcat依賴包 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project>
注:
1、依賴新增完之後,專案上右鍵->maven->Update Maven Project
2、專案上右鍵->Java EE Tools->Generate Deployment Descriptor Stub
web.xml
路徑:src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!-- 學習前置條件 --> <!-- 問題1:web.xml中servelet、filter、listener、context-param載入順序 --> <!-- 問題2:load-on-startup標籤的作用,影響了Servlet物件建立的時機 --> <!-- 問題3:url-pattern:標籤的配置方式有四種:/dispatcherServlet、/servlet/*、*.do、/ 以上四種配置--> <!-- 問題4:url-pattern標籤的配置為什麼配置/就不攔截jsp請求,而配置/*,就會攔截jsp請求 --> <!-- 問題4原因:標籤配置為/*報錯,因為它攔截了jsp請求,但是又不能處理jsp請求。 --> <!-- 問題5:配置了springmvc去讀取spring配置檔案之後,就產生了spring父子容器的問題 --> <!-- 配置前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 設定spring配置檔案路徑 --> <!-- 如果不設定初始化引數,那麼DispatcherServlet會讀取預設路徑下的配置檔案 --> <!-- 預設配置檔案路徑:/WEB-INF/springmvc-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- 指定初始化時機,設定為2,表示Tomcat啟動時,它會跟隨著啟動,DispatcherServlet會跟隨著初始化 --> <!-- 如果沒有指定初始化時機,DispatcherServlet就會在第一次被請求的時候,才會初始化,而且只會被初始化一次(單例模式) --> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- url-pattern的設定 --> <!-- 不要配置為/*,否則報錯 --> <!-- 通俗解釋:會攔截整個專案中的資源訪問,包含JSP和靜態資源的訪問,對於JS的訪問,springmvc提供了預設Handler處理器 --> <!-- 但是對於JSP來講,springmvc沒有提供預設的處理器,我們也沒有手動編寫對應的處理器,此時按照springmvc的處理流程分析得知,它down了 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
ItemController.java
路徑:/src/main/java/com/cyb/springmvc/controller/ItemController.java
package com.cyb.springmvc.controller; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.cyb.springmvc.po.item; /** * 處理器的開發方式有多種,比如實現HttpRequestHandler介面、Controller介面的方式、還有註解的方式 企業中使用的一般都是註解的方式 * 註解的注意事項 * 1、類上加上@Controller註解(必須是Controller,可以通過原始碼找到答案) * 2、類上或者方法上面要加上@RequestMapping(必須) * * @author apple * */ @Controller public class ItemController { //@RequestMapping此時填寫的是url //ModelAndView:Model標識的是資料型別,View就是最終要展示給使用者的檢視 @RequestMapping("queryItem") public ModelAndView queryItem() { //用靜態資料模型 List<item> itemList=new ArrayList<item>(); item item_1=new item(); item_1.setName("蘋果手機"); item_1.setPrice(5000); item_1.setDetail("iphoneX蘋果手機!"); itemList.add(item_1); item item_2=new item(); item_2.setName("華為手機"); item_2.setPrice(6000); item_2.setDetail("華為5G網速就是快!"); itemList.add(item_2); ModelAndView mvAndView=new ModelAndView(); //設定資料模型,相當於request的setAttribute方法,實質上,底層確實也是轉成了request() //先將k/v資料放入map中,最終根據檢視物件不同,再進行後續處理 mvAndView.addObject("itemList",itemList); //設定view檢視 mvAndView.setViewName("/WEB-INF/jsp/item/item-list.jsp"); return mvAndView; } }
item.java
路徑:src/main/java/com/cyb/springmvc/po/item.java
package com.cyb.springmvc.po; public class item { private String name; private double price; private String detail; public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } }
item-list.jsp
路徑:src/webapp/WEB-INF/jsp/item/item-list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>查詢商品列表</title> </head> <body> <form action="${pageContext.request.contextPath }/itemList.do" method="post"> 查詢條件: <table width="100%" border=1> <tr> <td><input type="submit" value="查詢" /></td> </tr> </table> 商品列表: <table width="100%" border=1> <tr> <td>商品名稱</td> <td>商品價格</td> <td>商品描述</td> <td>操作</td> </tr> <c:forEach items="${itemList }" var="item"> <tr> <td>${item.name }</td> <td>${item.price }</td> <td>${item.detail }</td> <td><a href="${pageContext.request.contextPath }/itemEdit.do?id=${item.name}">修改</a></td> </tr> </c:forEach> </table> </form> </body> </html>
專案結構圖
執行
完整專案
直接下載
SpringMVC 框架原始碼分析
框架結構
程式入口
一、初始化Servlet
二、處理器對映,渲染頁面
注:標記的方法體,跟蹤進去讀原始碼就好啦!~~
預設配置檔案
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ org.springframework.web.servlet.function.support.RouterFunctionMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\ org.springframework.web.servlet.function.support.HandlerFunctionAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
架構流程
- 使用者傳送請求至前端控制器DispatcherServlet
- DispatcherServlet收到請求呼叫HandlerMapping處理器對映器
- 處理器對映器根據請求url找到具體的處理器,生成處理器物件及處理器攔截器(如果有則生成)一併返回給DispatcherServlet
- DispatcherServlet通過HandlerAdapter處理器介面卡呼叫處理器
- HandlerAdapter執行處理器(handler,也叫後端控制器)
- Controller執行完成返回ModelAndView
- HandlerAdapter將handler執行結果ModelAndView返回給DispatcherServlet
- DispatcherServlet將ModelAndView傳給ViewReslover檢視解析器
- ViewReslover解析後返回具體View物件
- DispatcherServlet對View進行渲染檢視(即將模型資料填充至檢視種)
- DispatcherServlet響應使用者
元件說明
DispatcherServlet:前端控制器
使用者請求到達前端控制器,它就相當於mvc模式中的C,DispatcherServlet是整個流程控制的中心,由它呼叫其他元件處理使用者的請求,DispatcherServlet的存在降低了元件之間的耦合性。
HandlerMapping:處理器對映器
HandlerMapping負責根據使用者請求找到Handler即處理器,springmvc提供了不同的對映器實現不同的對映方式,例如:配置檔案方式,實現介面方式,註解方式等。
Handler:處理器
Handler是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下,Handler對具體的使用者請求進行處理。
由於Handler涉及到具體的使用者業務請求,所以一般情況需要程式設計師根據業務需求開發Handler。
HandlerAdapter:處理器介面卡
通過HandlerAdapter對處理器進行執行,這是介面卡模式的應用,通過擴充套件介面卡可以對更多型別的處理器進行執行。
View Resolver:檢視解析器
View Resolver負責將處理結果生成View檢視,View Resolver首先根據邏輯檢視名解析成物理檢視名即具體的頁面地址,再生成View檢視物件,最後對View進行渲染將處理結果通過頁面展示給使用者。
View:檢視
springmvc框架提供了很多View檢視型別的支援,包括:jstlView、freemarkerView、pdfView等。我們最常用的檢視就是jsp。
一般情況下需要通過頁面標籤或頁面模板技術將模型資料通過頁面展示給使用者,需要由程式設計師根據業務需求開發具體的頁面。
說明
再springmvc的各個元件中,處理器對映器、處理器介面卡、檢視解析器稱為springmvc的三大元件。需要使用者開發的元件有:處理器、檢視
三大元件配置(註解方式)
註解對映器和介面卡
通過bean標籤配置
RequestMappingHandlerMapping:註解式處理器對映器
對類中標記@ResquestMapping的方式進行對映,根據ResquestMapping定義的url匹配ResquestMapping標記的方法,匹配成功返回HandlerMethod物件給前端控制器,HandlerMethod物件中封裝url對應的方法Method。
配置如下:
<!--註解對映器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <!--註解介面卡 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
通過mvc標籤配置(推薦)
<mvc:annotation-drivern />
mvc:annotation-drivern標籤的作用,詳見AnnotationDrivenBeanDefinitionParser類的parse方法。分析原始碼可知:mvc:annotation-drivern往spring容器中註冊以下的一些BeanDefinition
- ContentNegotiationManagerFactoryBean
- RequestMappingHandlerMapping
- ConfigurableWebBindingInitializer
- RequestMappingHandlerAdapter
- CompositeUriComponentsContributorFactoryBean
- ConversionServiceExposingInterceptor
- MappedInterceptor
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
- BeanNameUrlHandlerMapping
- HttpRequestHandlerAdapter
- SimpleControllerHandlerAdapter
- HandlerMappingIntrospector
檢視解析器
再springmvc.xml檔案配置如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 該檢視解析器,預設的檢視類就是JstlView,可以不寫 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
- InternalResourceViewResolver:預設支援JSP檢視解析
- viewClass:JstlView表示JSP模板頁面需要使用JSTL標籤庫,所以classpath中必須包含jstl的相關jar 包。此屬性可以不設定,預設為JstlView。
- prefix 和suffix:查詢檢視頁面的字首和字尾,最終檢視的址為:字首+邏輯檢視名+字尾,邏輯檢視名需要在controller中返回的ModelAndView指定,比如邏輯檢視名為hello,則最終返回的jsp檢視地址 “WEB-INF/jsp/hello.jsp”