SpringMvc 是一種底層基於 Servlet 實現 MVC 模型的輕量級 Web 開發框架,是一種側重於表現層的開發技術。
SpringMvc 使用起來比 Servlet 要方便很多,性能也很不錯,常用於小型項目的快速搭建和開發。本篇博客不會介紹有關 MVC 的理論知識,主要側重於代碼實踐,采用純注解的方式快速搭建 SpringMvc 工程,並展示常用的接收請求參數的方式。在本篇博客的最后會提供 Demo 的源代碼。
有關 SpringMvc 的詳細使用可參考官網:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc
一、搭建工程
新建一個 maven 項目,導入相關 jar 包,我所導入的 jar 包都是最新的,內容如下:
有關具體的 jar 包地址,可以在 https://mvnrepository.com 上進行查詢。
<dependencies>
<!--導入 servlet 相關的 jar 包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!--導入 Spring 核心 jar 包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!--導入 SpringMvc 的 jar 包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
</dependencies>
配置好引用的 jar 包后,打開右側的 Maven 窗口,刷新一下,這樣 Maven 會自動下載所需的 jar 包文件。
搭建好的項目工程整體目錄比較簡單,具體如下圖所示:
項目工程結構簡單介紹:
com.jobs.config 包下存儲的是 SpringMvc 的配置文件和 Servlet 的初始化文件
com.jobs.controller 包下存儲的是用於提供 http 請求的類
com.jobs.domain 包下存儲的是 JavaBean 實體類
web 目錄下放置的是網站文件,其中 statics 目錄下存放是靜態資源文件
二、SpringMvc 配置相關
com.jobs.config 下的 SpringMvcConfig 類是 SpringMvc 的配置類,具體內容如下:
package com.jobs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@ComponentScan("com.jobs")
//啟用 mvc 功能,配置了該注解之后,SpringMvc 攔截器放行相關資源的設置,才會生效
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
//配置 SpringMvc 攔截器放行的資源類型
/*@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/statics/**").addResourceLocations("/statics/");
//registry.addResourceHandler("/statics/css/**").addResourceLocations("/statics/css/");
//registry.addResourceHandler("/statics/img/**").addResourceLocations("/statics/img/");
//registry.addResourceHandler("/statics/js/**").addResourceLocations("/statics/js/");
}*/
//配置 SpringMvc 連接器放行常用資源的格式(圖片,js,css)
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//注解配置 SpringMvc 返回配置的字符串所表示的頁面,從哪些去找
//可以注釋掉下面的方法,這樣需要在 SpringMvc 方法返回時,指定全局路徑的頁面地址
//這里配置的是:根據 SpringMvc 方法返回的字符串,到 /WEB-INF/pages/ 下找對應名稱的 jsp 頁面
@Bean
public InternalResourceViewResolver getViewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
//如果頁面需要使用JSTL標簽庫的話
//viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
}
這里需要注意以下幾項:
- 使用注解 @ComponentScan 中的 includeFilters 指定 SpringMvc 只掃描裝載帶有 @Controller 注解的類,其它的注解不需要給 SpringMvc 掃描,其它的注解全部交給 Spring 來掃描裝載。
- 必須在 SpringMvc 的配置類上添加 @EnableWebMvc 注解,這樣才能夠有機會訪問到靜態資源(圖片、css、js)。
- 可以重寫 configureDefaultServletHandling 方法,只需要一句代碼 configurer.enable() 即可讓 SpringMvc 攔截器放行靜態資源的訪問限制。
SpringMvc 技術底層是基於 Servlet 實現的,因此必須讓 Servlet 裝載才能實現,我們編寫 ServletInitConfig 類初始化 Servlet 容器,裝載 SpringMvc 的配置,具體內容如下:
package com.jobs.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;
public class ServletInitConfig extends AbstractDispatcherServletInitializer {
//初始化 Servlet 容器,加載 SpringMvc 配置類
//創建 web 專用的 Spring 容器對象:WebApplicationContext
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext cwa = new AnnotationConfigWebApplicationContext();
//裝載 SpringMvc 的配置
cwa.register(SpringMvcConfig.class);
return cwa;
}
//配置 SpringMvc 的 DispatcherServlet 攔截地址,攔截所有請求
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
//在servlet容器啟動時進行配置統一字符編碼,防止接收到的中文參數值是亂碼
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//這行代碼不能省略
super.onStartup(servletContext);
//設置【獲取到的請求】的統一字符編碼過濾器,
//無論是【請求、轉發、包含】統一使用 UTF-8 編碼
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
//注意:這里的過濾器名稱必須是 characterEncodingFilter
FilterRegistration.Dynamic registration =
servletContext.addFilter("characterEncodingFilter", cef);
registration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST,
DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*");
}
}
經過上面兩個類的處理,SpringMvc 純注解方式已經配置完畢,下面就可以使用 SpringMvc 技術處理請求了。
三、驗證搭建成果
我們編寫 ReqController1 類來驗證 SpringMvc 搭建成果,具體內容如下:
package com.jobs.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
//@RequestMapping 注解可以在類上使用,也可以在類中具體的方法上使用
@RequestMapping("/req1")
@Controller
public class ReqController1 {
//使用配置的前綴和后綴
@RequestMapping("/test1")
public String SpringMvcTest1() {
System.out.println("SpringMvc 搭建成功...默認使用所配置的前綴和后綴");
//由於在 SpringMvcConfig 類中的 getViewResolver 方法,配置了前綴和后綴
//因此這里如果返回字符串的話,默認會到 /WEB-INF/pages/ 下找對應名稱的 jsp 頁面
//由於這里返回字符串 success,因此直接找 /WEB-INF/pages/success.jsp 頁面
return "success";
}
//臨時不想使用所配置的前綴和后綴
@RequestMapping("test2")
public String SpringMvcTest2() {
System.out.println("SpringMvc 如果不想使用所配置的前綴和后綴,可以使用 forward 或 redirect");
//當使用了 forward 或 redirect 時,將不使用所配置的前綴和后綴
return "forward:/ok.html";
//return "redirect:/ok.html";
}
//訪問靜態資源
@RequestMapping(value = "test3")
public String SpringMvcTest3() {
System.out.println("SpringMvc 查看靜態資源");
//由於在 SpringMvcConfig 類中重寫了 configureDefaultServletHandling 方法,
//啟用了常用的靜態資源訪問,所以 SpringMvc 不會攔截靜態資源的訪問
//在 picture.jsp 頁面,可以看到 css 樣式效果,圖片展示,以及外部 js 引用后可以運行
return "picture";
}
}
上面用到的靜態資源內容如下:
test.css 的內容為:
h1{
color: coral;
}
test.js 的內容為:
function testalert() {
alert('努力學習,天天向上!!!');
}
所用到的頁面內容如下:
success.jsp 頁面代碼為:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>請求成功</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/statics/css/test.css">
</head>
<body>
<h1>這里是 jsp 動態頁面:success.jsp</h1>
<h1>請求成功,請查看控制台上打印的結果...</h1>
</body>
</html>
picture.jsp 頁面代碼為:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/statics/css/test.css">
</head>
<body>
<h1>這里是 picture.jsp 展示靜態資源(圖片,css,js)的訪問:</h1>
<img src="${pageContext.request.contextPath}/statics/img/study.jpg"/>
<input type="button" onclick="testalert()" value="點擊我,運行js彈出框"/>
<!--建議把 js 文件放在網頁的最下面,提高網頁的打開速度-->
<script src="${pageContext.request.contextPath}/statics/js/test.js"></script>
</body>
</html>
ok.html 靜態頁面的代碼為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>請求成功</title>
<link rel="stylesheet" href="/statics/css/test.css">
</head>
<body>
<h1>這里是靜態頁面:ok.html</h1>
<h1>請求成功,請查看控制台上打印的結果...</h1>
</body>
</html>
四、請求參數的接收
首先列出 Employee 實體類,因為下面的代碼會演示使用實體類作為接收參數,來接收請求參數中的值,然后編寫 ReqController2 類,來演示在實際開發中,常用的一些請求參數的接收方式,具體細節如下:
package com.jobs.domain;
import java.util.List;
public class Employee {
//姓名
private String name;
//年齡
private Integer age;
//愛好
private List<String> hobby;
//這里省略了各個字段的 get 和 set 方法...
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", hobby=" + hobby +
'}';
}
}
package com.jobs.controller;
import com.jobs.domain.Employee;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
//@RequestMapping 注解可以在類上使用,也可以在類中具體的方法上使用
//@RequestMapping("req2")
@Controller
public class ReqController2 {
//請求參數與接收參數,名稱必須一致,如果參數缺少,則參數取類型默認值
//http://localhost:8080/request1?name=jobs
//http://localhost:8080/request1?name=jobs&age=33
@RequestMapping("/request1")
public String request1(String name, Integer age) {
String result = "name=" + name + ",age=" + age;
System.out.println(result);
return "success";
}
//設置請求參數的名稱,以及是否為必須提供的參數,以及設置參數的默認值
//http://localhost:8080/request2 報錯,沒有提供必填參數 userName
//http://localhost:8080/request2?name=jobs
//http://localhost:8080/request2?name=jobs&age=33
@RequestMapping("/request2")
public String request2(
@RequestParam(value = "userName", required = true) String name,
@RequestParam(value = "userAge", defaultValue = "30") Integer age) {
String result = "name=" + name + ",age=" + age;
System.out.println(result);
return "success";
}
//請求參數名稱,與接收參數的 JavaBean 類的字段名稱相同的話,
//則 SpringMvc 會根據請求參數自動給 JavaBean 實例化的對象屬性賦值
//http://localhost:8080/request3?name=jobs
//http://localhost:8080/request3?name=jobs&age=33
@RequestMapping("/request3")
public String request3(Employee emp) {
String result = "name=" + emp.getName() + ",age=" + emp.getAge();
System.out.println(result);
return "success";
}
//如果接收參數中,普通參數名稱 與 JavaBean 類的字段名稱相同的話,則都會進行賦值
//http://localhost:8080/request4?name=jobs&age=33
@RequestMapping("/request4")
public String request4(Employee emp, String name, Integer age) {
StringBuilder sb = new StringBuilder();
sb.append("使用 JavaBean 類作為參數,獲取的結果:");
sb.append("name=" + emp.getName() + ",age=" + emp.getAge());
sb.append("\r\n");
sb.append("使用普通參數獲取的結果:");
sb.append("name=" + name + ",age=" + age);
String result = sb.toString();
System.out.println(result);
return "success";
}
//使用數組作為參數,接收傳入的數組數據
//http://localhost:8080/request5?hobby=唱歌&hobby=跳舞&hobby=打游戲
@RequestMapping("/request5")
public String request5(String[] hobby) {
if (hobby != null && hobby.length > 0) {
for (String h : hobby) {
System.out.println(h);
}
} else {
System.out.println("未獲取到所傳入的 hobby 數據");
}
return "success";
}
//使用集合作為參數,接收傳入的數組數據,無法直接獲取數組數據
//必須要使用 @RequestParam 注解將請求參數與接收參數進行名稱映射
//http://localhost:8080/request6?hobby=唱歌&hobby=跳舞&hobby=打游戲
@RequestMapping("/request6")
public String request6(
@RequestParam(value = "hobby") List<String> hobbylist) {
if (hobbylist != null && hobbylist.size() > 0) {
for (String hobby : hobbylist) {
System.out.println(hobby);
}
} else {
System.out.println("未獲取到所傳入的 hobby 數據");
}
return "success";
}
//使用 JavaBean 類作為接收參數,接收數組數據
//http://localhost:8080/request7?hobby=唱歌&hobby=跳舞&hobby=打游戲
@RequestMapping("/request7")
public String request7(Employee emp) {
List<String> hobbylist = emp.getHobby();
if (hobbylist != null && hobbylist.size() > 0) {
for (String hobby : hobbylist) {
System.out.println(hobby);
}
} else {
System.out.println("未獲取到所傳入的 hobby 數據");
}
return "success";
}
//發來的請求數據,如果是日期類型,如果想直接使用日期類型的參數進行接收的話,
//可以根據傳入數據的日期格式,明確指定好接收數據的對應格式,就可以接收到具體的日期數據
//http://localhost:8080/request8?date=2022-01-21 15:20:30
@RequestMapping("/request8")
public String request8(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH時mm分ss秒");
String dateString = sdf.format(date);
System.out.println(dateString);
return "success";
}
//可以接收提交過來的 body 數據,必須使用 Post 請求方式
//默認情況下,各種請求方式都支持,可以通過 method 指定允許的請求方式
//這里可以使用 PostMan 來請求該接口,必須使用 Post 請求方式從 body 中獲取數據
//http://localhost:8080/request9
@RequestMapping(value = "/request9", method = RequestMethod.POST)
public String request9(@RequestBody String json) {
System.out.println(json);
return "success";
}
}
上面的請求方式代碼中,最后一個請求方法,從 body 中獲取數據,需要采用 Post 請求才可以。這里可以使用 PostMan 工具進行測試,在 PostMan 工具中可以 Post 發送各種類型的數據,這里僅僅以 Post 發送 Json 數據為例,因為在實際開發中,向服務器發送 Json 格式的數據比較常用,使用 PostMan 工具請求最后一個接口的細節如下所示:
在 IDEA 工具的控制台上打印出 Post 請求發送過來的數據:
到此為止,SpringMvc 采用純注解方式快速搭建已經介紹完畢。在 SpringMvc 的純注解配置中實現了監控所有請求,並忽略和放行常見靜態資源的請求,通過配置前綴和后綴,實現通過返回的字符串自動查找對應路徑下的 jsp 頁面。最后列出了常用的接收傳入的參數值的幾種方式。
本篇博客的 demo 源代碼下載地址為:https://files.cnblogs.com/files/blogs/699532/SpringMVC_Request.zip