1、什么是 SpringMVC?
SpringMVC 是基于 MVC 開發模式的框架,用來優化控制器,它是 Spring 家族的一員,它也具備 IOC 和 AOP,
- 什么是MVC?
MVC 是一種開發模式,它是模型視圖控制器的簡稱,所有的 web 應用都是基于 MVC 開發,
1)M:模型層,包含物體類,業務邏輯層,資料訪問層
2)V:視圖層,html,javaScript,vue 等都是視圖層,用來顯現資料
3)C:控制器,它是用來接收客戶端的請求,并回傳回應到客戶端的組件,Servlet就是組件
2、SpringMVC 框架的優點?
1)輕量級,基于 MVC 的框架
2)易于上手,容易理解,功能強大
3)它具備 IOC 和 AOP
4)完全基于注解開發
3、Springmvc 的執行流程:

執行流程說明:
1)向服務器發送HTTP請求,請求被前端控制器 DispatcherServlet 捕獲,
2)DispatcherServlet 根據<servlet-name>中的配置對請求的URL進行決議,得到請求資源識別符號(URI),然后根據該URI,呼叫 HandlerMapping 獲得該Handler配置的所有相關的物件(包括Handler物件以及Handler物件對應的攔截器),最后以 HandlerExecutionChain 物件的形式回傳,
3)DispatcherServlet 根據獲得的 Handler,選擇一個合適的 HandlerAdapter,
4)提取 Request 中的模型資料,填充 Handler 入參,開始執行 Handler(Controller),在填充 Handler的入參程序中,根據你的配置,Spring 將幫你做一些額外的作業:
HttpMessageConveter:將請求訊息(如Json、xml等資料)轉換成一個物件,將物件轉換為指定的回應資訊,
資料轉換:對請求訊息進行資料轉換,如String轉換成Integer、Double等,
資料格式化:對請求訊息進行資料格式化,如將字串轉換成格式化數字或格式化日期等,
資料驗證:驗證資料的有效性(長度、格式等),驗證結果存盤到BindingResult或Error中,
5)Handler(Controller)執行完成后,向 DispatcherServlet 回傳一個 ModelAndView 物件,
6)根據回傳的ModelAndView,選擇一個適合的 ViewResolver(必須是已經注冊到Spring容器中的ViewResolver)回傳給DispatcherServlet,
7)ViewResolver 結合Model和View,來渲染視圖,
8)視圖負責將渲染結果回傳給客戶端
4、基于注解的 SpringMVC 框架開發的步驟:
1)新建專案,選擇 webapp 模板
2)修改目錄,添加缺失的 test 目錄,java目錄,resources目錄(兩套),并修改目錄屬性
3)修改 pom.xml 檔案,添加 SpringMVC 的依賴,添加 Servlet 的依賴
<!--添加springmvc的依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--添加servlet的依賴-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
4)添加 springmvc.xml 組態檔,指定包掃描,添加視圖決議器.
SpringMVC 框架為了避免請求資源路徑與擴展名上的冗余,在視圖決議器 InternalResouceViewResolver 中引入了請求的前輟與后輟,而 action 中只需給出要跳轉頁面的檔案名即可,對于具體的檔案路徑與檔案擴展名,視圖決議器會自動完成拼接;
<context:component-scan>:用來進行包掃描,這里用于指定@Controller注解所在的包路徑,
<!--添加包掃描-->
<context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
<!--添加視圖決議器-->
<bean >
<!--配置前綴-->
<property name="prefix" value="https://www.cnblogs.com/admin/"></property>
<!--配置后綴-->
<property name="suffix" value="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/.jsp"></property>
</bean>
5)洗掉 web.xml 檔案,新建 web.xml
6)在 web.xml 檔案中注冊 springMVC 框架(所有的 web 請求都是基于 servlet 的)
<!--注冊SpringMVC框架-->
<servlet>
<servlet-name>springmvc</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>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
指定攔截什么樣的請求
<a href="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/${pageContext.request.contextPath}/demo.action">訪問服務器</a>
-->
<!--指定攔截以.action結尾的請求,交給核心處理器DispatcherServlet處理,-->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
7)在 webapp 目錄下新建 admin 目錄,在 admin 目錄下新建 main.jsp 頁面,洗掉 index.jsp 頁面,并新建
8)開發控制器(Servlet),這是一個普通的類,不用繼承和實作介面,類中的每個方法就是一個具體的action控制器,
@Controller //表示當前類為處理器,交給Spring去創建物件
public class DemoAction {
/**
* 以前的Servlet的規范
* protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
* action中所有的功能實作都是由方法來完成的
* action方法的規范
* 1)訪問權限是public
* 2)方法的回傳值任意
* 3)方法名稱任意
* 4)方法可以沒有引數,如果有可是任意型別
* 5)要使用@RequestMapping注解來宣告一個訪問的路徑(名稱)
*
*/
@RequestMapping("/demo")
public String demo(){
System.out.println("服務器被訪問到了.......");
return "main"; //可以直接跳到/admin/main.jsp頁面上
}
}
9)添加 tomcat 進行測驗功能
5、分析 web 請求:
web請求執行的流程:
index.jsp<--------------->DispatcherServlet<------------------->SpringMVC的處理器(加了@Controller的類中的方法)
one.jsp <--------------->DispatcherServlet<------------------->SpringMVC的處理器(加了@Controller的類中的方法)
- DispatcherServlet 要在 web.xml 檔案中注冊才可用
6、@RequestMapping 注解詳解:
- 此注解就是來映射服務器訪問的路徑,@RequestMapping 的 value 屬性用于定義所匹配請求的 URI,
1)此注解可加在方法上,是為此方法注冊一個可以訪問的名稱(路徑),
@RequestMapping("/demo")
public String demo(){
System.out.println("服務器被訪問到了.......");
return "main"; //可以直接跳到/admin/main.jsp頁面上
}
//<a href="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/${pageContext.request.contextPath}/demo.action">訪問服務器</a>
2)此注解可以加在類上,相當于是包名(虛擬路徑),區分不同類中相同的 action 的名稱,URI 的請求是相對于 Web 的根目錄,換個角度說,要訪問處理器的指定方法,必須要在方法指定 URI 之前加上處理器類前定義的模塊名稱,
@RequestMapping("/user")
public class DemoAction1 {..}
//<a href="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/${pageContext.request.contextPath}/user/demo.action">訪問服務器</a>
3)此注解可區分 get 請求和 post 請求:
- 對于@RequestMapping,其有一個屬性 method,用于對被注解方法所處理請求的提交方式進行限制,即只有滿足該 method 屬性指定的提交方式的請求,才會執行該被注解方法,
@Controller
public class ReqAction {
@RequestMapping(value = "https://www.cnblogs.com/req",method = RequestMethod.GET)
public String req(){
System.out.println("我是處理get請求的........");
return "main";
}
@RequestMapping(value = "https://www.cnblogs.com/req" ,method = RequestMethod.POST)
public String req1(){
System.out.println("我是處理post請求的........");
return "main";
}
}
7、五種資料提交方式:
- 前四種資料注入的方式,會自動進行型別轉換,但無法自動轉換日期型別,
1)單個提交資料
- 在方法中宣告一個和表單提交的引數名稱相同的引數,由框架按照名稱直接注入,
栗子:
/*
<form action="${pageContext.request.contextPath}/one.action">
姓名:<input name="myname"><br>
年齡:<input name="age"><br>
<input type="submit" value="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/提交">
</form>
*/
@RequestMapping("/one")
public String one(String myname,int age){ ===>自動注入,并且型別轉換
System.out.println("myname="+myname+",age="+(age+100));
return "main";
}
2)物件封裝提交資料:
-
宣告一個自定義的物體類引數,框架呼叫物體類中相應的setter方法注入屬性值,只要保證物體類中成員變數的名稱與提交請求的name屬性值一致即可,
物體類:
public class Users {
private String name;
private int age;
//構造方法,setter方法等省略,,,
}
頁面:
<form action="${pageContext.request.contextPath}/two.action" method="post">
姓名:<input name="name"><br>
年齡:<input name="age"><br>
<input type="submit" value="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/提交">
</form>
action:
@RequestMapping("/two")
public String two(Users u){
System.out.println(u);
return "main";
}
3)動態占位符提交
- 僅限于超鏈接或地址攔提交資料,它是一杠一值,一杠一大括號,使用注解 @PathVariable 來決議,
//<a href="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/${pageContext.request.contextPath}/three/張三/22.action">動態提交</a>
@RequestMapping("/three/{uname}/{uage}")
public String three(@PathVariable("uname") //用來決議路徑中的請求引數
String name,
@PathVariable("uage")
int age){
System.out.println("name="+name+",age="+(age+100));
return "main";
}
4)映射名稱不一致
- 提交請求引數與action方法的形參的名稱不一致,使用注解@RequestParam來決議
/**
* 姓名:<input name="name"><br>
* 年齡:<input name="age"><br>
*/
@RequestMapping("/four")
public String four(@RequestParam("name") //專門用來解決名稱不一致的問題
String uname,
@RequestParam("age")
int uage){
System.out.println("uname="+uname+",uage="+(uage+100));
return "main";
}
5)使用HttpServletRequest物件提取;
/**
* 姓名:<input name="name"><br>
* 年齡:<input name="age"><br>
*/
@RequestMapping("/five")
public String five(HttpServletRequest request){
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
System.out.println("name="+name+",age="+(age+100));
return "main";
}
8、中文亂碼解決方案:
- 對于前面所接收的請求引數,若含有中文,則會出現中文亂碼問題,Spring 對于請求引數中的中文亂碼問題,給出了專門的字符集過濾器:
- spring-web-5.2.5.RELEASE.jar 的 org.springframework.web.filter 包下的 CharacterEncodingFilter 類,
解決方案:
- 在 web.xml 中注冊字符集過濾器,即可解決 Spring 的請求引數的中文亂碼問題,不過,最好將該過濾器注冊在其它過濾器之前,因為過濾器的執行是按照其注冊順序進行的,
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--
配置引數
private String encoding;
private boolean forceRequestEncoding;
private boolean forceResponseEncoding;
-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
9、action方法的回傳值:
1)String:
處理器方法回傳的字串可以指定邏輯視圖名,通過視圖決議器決議可以將其轉換為物理視圖地址;
可以自動拼接前綴和后綴,可以指定回傳的路徑;
當然,也可以直接回傳資源的物理視圖名,不過,此時就不需要再在視圖決議器中再配置前輟與后輟了,
2)Object:
回傳 json 格式的物件,自動將物件或集合轉為 json,使用的 jackson 工具進行轉換,必須要添加 jackson 依賴,一般用于 ajax 請求;
處理器方法也可以回傳 Object 物件,這個 Object 可以是 Integer,自定義物件,Map,List 等,但回傳的物件不是作為邏輯視圖出現的,而是作為直接在頁面顯示的資料出現的,回傳物件,需要使用@ResponseBody 注解,將轉換后的 JSON 資料放入到回應體中,Ajax請求多用于Object回傳值型別,
3)void:
無回傳值,一般用于ajax請求;
若處理器對請求處理后,無需跳轉到其它任何資源,此時可以讓處理器方法回傳 void,
4)基本資料型別,用于 ajax 請求,
5)ModelAndView:回傳資料和視圖物件,現在用的很少,
10、完成 ajax 請求訪問服務器,回傳學生集合:
1)添加jackson依賴
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
2)在 webapp 目錄下新建 js目錄,添加 jQuery 函式庫
3)在 index.jsp 頁面上匯入函式庫:
4)在 action 上添加注解@ResponseBody,用來處理ajax請求
@Controller
public class AjaxAction {
//處理ajax請求,一定要加@ResponseBody
@ResponseBody
@RequestMapping("/ajax")
public List<Student> ajax(){
Student stu1 = new Student("張三",22);
Student stu2 = new Student("李四",24);
Student stu3 = new Student("王五",23);
List<Student> list = new ArrayList<>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
//呼叫json轉換工具ObjectMapper進行轉換
return list; //===>springmvc負責轉換成json
}
}
5)在 springmvc.xml 檔案中添加注解驅動 <mvc:annotationdriven/>,它用來決議 @ResponseBody 注解
<mvc:annotation-driven></mvc:annotation-driven>
11、SpringMVC 的四種跳轉方式:
- 本質就是兩種跳轉:請求轉發和重定向,衍生出四種是請求轉發頁面,轉發action,重定向頁面,重定向action;
- 默認的跳轉是請求轉發,直接跳轉到 jsp 頁面展示;
可以使用框架提供的關鍵字 redirect: ,進行一個重定向操作,包括重定向頁面和重定向action;
可以使用框架提供的關鍵字forward:,進行服務器內部轉發操作,包括轉發頁面和轉發action;
- 當使用 redirect: 和 forward: 關鍵字時,視圖決議器中前綴后綴的拼接就無效了,
栗子:
@RequestMapping("/one")
public String one(){
System.out.println("這是請求轉發頁面跳轉.........");
return "main"; //默認是請求轉發,使用視圖決議器拼接前綴后綴進行頁面跳轉
}
@RequestMapping("/two")
public String two(){
System.out.println("這是請求轉發action跳轉.........");
// /admin/ /other.action .jsp
//forward: 這組字串可以屏蔽前綴和后綴的拼接.實作請求轉發跳轉
return "forward:/other.action"; //默認是請求轉發,使用視圖決議器拼接前綴后綴進行頁面跳轉
}
@RequestMapping("/three")
public String three(){
System.out.println("這是重定向頁面.......");
//redirect: 這組字串可以屏蔽前綴和后綴的拼接.實作重定向跳轉
return "redirect:/admin/main.jsp";
}
@RequestMapping("/four")
public String four(){
System.out.println("這是重定向action.......");
//redirect: 這組字串可以屏蔽前綴和后綴的拼接.實作重定向跳轉
return "redirect:/other.action";
}
@RequestMapping("/five")
public String five(){
System.out.println("這是隨便跳.......");
return "forward:/fore/login.jsp";
}
12、SpringMVC 默認的引數型別:
不需要去創建,直接拿來使用即可,
1)HttpServletRequest
2)HttpServletResponse
3)HttpSession
4)Model
5)Map
6)ModelMap
栗子:
//做一個資料,傳到main.jsp頁面上
Users u = new Users("張三",22);
//傳遞資料
request.setAttribute("requestUsers",u);
session.setAttribute("sessionUsers",u);
model.addAttribute("modelUsers",u);
map.put("mapUsers",u);
modelMap.addAttribute("modelMapUsers",u);
注意:Map,Model,ModelMap 和 request 一樣,都使用請求作用域進行資料傳遞,所以服務器端的跳轉必須是請求轉發,頁面才可以得到值,
13.日期處理:
1)日期的提交處理
A、單個日期處理
- 要使用注解@DateTimeFormat,此注解必須搭配 springmvc.xml 檔案中的 <mvc:annotationdriven標簽>
在方法的引數上使用@DateTimeFormat注解;
在類的成員setXXX()方法上使用@DateTimeFormat注解
- 但這種解決方案要在每個使用日期型別的地方都去添加使用@DateTimeFormat注解,比較麻煩,我們可以使用@InitBinder注解來進行類中統一日期型別的處理,
B.類中全域日期處理
注冊一個注解,用來決議本類中所有的日期型別,自動轉換
@InitBinder;
public void initBinder(WebDataBinder dataBinder){
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
}
2)日期的顯示處理:
- 在頁面上顯示好看的日期,必須使用 JSTL
步驟:
A)添加依賴 jstl
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
B)在頁面上匯入標簽庫 :
- 如果是單個日期物件,直接轉為好看的格式化的字串進行顯示
如果是 list 中的物體類物件的成員變數是日期型別,則必須使用 jstl 進行顯示,
<%--匯入jstl核心標簽庫--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--匯入jstl格式化標簽庫--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
C)使用標簽顯示資料:
<table border="1">
<tr>
<th>姓名</th>
<th>生日</th>
</tr>
<c:forEach items="${list}" var="stu">
<tr>
<td>${stu.name}</td>
<td>${stu.birthday}------ <fmt:formatDate value="https://www.cnblogs.com/Burning-youth/archive/2022/06/04/${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate></td>
</tr>
</c:forEach>
</table>
14、SpringMVC 的攔截器:
- 針對請求和回應進行的額外的處理,在請求和回應的程序中添加預處理,后處理和最終處理
攔截器執行的時機:
1)preHandle():在請求被處理之前進行操作(預處理)
2)postHandle():在請求被處理之后,但結果還沒有渲染前進行操作,可以改變回應結果(后處理)
3)afterCompletion:所有的請求回應結束后執行善后作業,清理物件,關閉資源(最終處理)
攔截器實作的兩種方式:
1)繼承 HandlerInterceptorAdapter 的父類
2)實作 HandlerInterceptor 介面
攔截器實作的步驟:
1)創建登錄方法,在 session 中存盤用戶資訊,用于進行權限驗證
@RequestMapping("/login")
public String login(String name, String pwd, HttpServletRequest request){
if("zar".equalsIgnoreCase(name) && "123".equalsIgnoreCase(pwd)){
//在session中存盤用戶資訊,用于進行權限驗證
request.getSession().setAttribute("users",name);
return "main";
}else{
request.setAttribute("msg","用戶名或密碼不正確!");
return "login";
}
}
2)開發攔截器的功能,實作 HandlerInterceptor 介面,重寫 preHandle() 方法:
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//是否登錄過的判斷
if(request.getSession().getAttribute("users") == null){
//此時就是沒有登錄,打回到登錄頁面,并給出提示
request.setAttribute("msg","您還沒有登錄,請先去登錄!");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
return true;//放行請求
}
}
3)在 springmvc.xml 檔案中注冊攔截器:
<mvc:interceptors>
<mvc:interceptor>
<!--映射要攔截的請求-->
<mvc:mapping path="/**"/>
<!--設定放行的請求-->
<mvc:exclude-mapping path="/showLogin"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/login"></mvc:exclude-mapping>
<!--配置具體的攔截器實作功能的類-->
<bean ></bean>
</mvc:interceptor>
</mvc:interceptors>
資源在WEB-INF目錄下:
很多企業會將動態資源放在WEB-INF目錄下,這樣可以保證資源的安全性,因為在WEB-INF目錄下的動態資源不可以直接訪問,必須要通過請求轉發的方式進行訪問,這樣避免了通過地址欄直接對資源的訪問,重定向也無法訪問動態資源,
栗子:
@Controller
public class WebInfAction {
@RequestMapping("/showMain")
public String showMain(){//通過請求轉發的方式訪問webapp目錄下的WEB-INF目錄下的main.jsp
System.out.println("訪問main.jsp");
return "main";
}
@RequestMapping("/showLogin")
public String showLogin(){////通過請求轉發的方式訪問webapp目錄下的WEB-INF目錄下的login.jsp
System.out.println("訪問login.jsp");
return "login";
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/486614.html
標籤:其他
