Spring MVC的基本使用1
1、Spring MVC的基本介紹
spring mvc 是基于 spring 的一個框架,實際上就是 spring 的一個模塊,是專門用來做 web 開發的。spring mvc 的底層實際上還是 servlet ,只是在 servlet 的基礎上面加入了一些功能,讓 web 開發更加方便,可以理解為是 servlet 的升級。
Spring MVC 框架是圍繞一個 DispatcherServlet(中央調度器) 來設計的,這個Servlet會把請求分發給各個處理器,由各個處理器來處理請求(處理器就是應用中注解了 @Controller 和 @RequestMapping 的類和方法)。DispatcherServlet其實就是個Servlet(它繼承自HttpServlet基類),DispatcherServlet 也被稱之為前端控制器。
DispatcherServlet處理請求的工作流如下:

2、SpringMVC的基本使用
2.1、springmvc的使用
先在 idea 中通過 maven 創建一個 web 項目,創建完成后我們可以手動添加 src/main/java 和 src/main/resource 目錄:

然后引入依賴,依賴配置文件 pom.xml 類似下面:
<?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>maven_ee_test01</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency> </dependencies> </project>
在 maven 的 web 項目的 web.xml 配置文件中配置中央調度器。默認生成的 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"> <!-- SpringMVC的前端控制器 --> <servlet> <servlet-name>springmvcTest</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 可以自定義springmvc讀取的配置文件的位置 --> <!-- <init-param>--> <!-- <param-name>contextConfigLocation</param-name>--> <!-- <param-value>/WEB-INF/xxx.xml</param-value> <!–指定配置文件的位置–>--> <!-- </init-param>--> <!-- 指定該servlet對象在tomcat啟動時即創建。 load-on-startup:指定tomcat啟動后創建對象的順序,tomcat會根據各個servlet的該屬性值按照順序來創建各個servlet對象。它的值是大于等于0的整數,值越小創建時間越早。--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvcTest</servlet-name> <!-- 使用框架時,url-pattern一般來說有兩種寫法: 1)使用自定義擴展名:*.do、*.action、*.mvc等等,如<url-pattern>*.do</url-pattern> 2)直接使用斜桿:<url-pattern>/</url-pattern>,表示攔截所有請求 --> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
DispatcherServlet 實際上就是個 servlet(它繼承自HttpServlet基類),在被創建時,會執行該 servlet 的 init() 方法。在 DispatcherServlet 的初始化過程中,該框架會嘗試拿到項目中的 WebContent/WEB-INF 目錄文件下的名為 [servlet-name]-servlet.xml 的配置文件,并創建其中所定義的bean。(比如是上面的配置,則該配置文件將會是 webcontent/WEB-INF/springmvcTest-servlet.xml,springmvc 會從該配置文件中加載應用程序上下文)。當然,我們也可以通過 init-param 標簽自定義配置文件的位置及名稱,不使用默認的。
然后我們需要在 webcontent(webapp)/WEB-INF 目錄下創建一個 spring 配置文件,并在該文件中開啟組件掃描。比如下面的 springmvcTest-servlet.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--開啟組件掃描。base-package寫包名,若要掃描多個包,可以用逗號隔開,或者直接寫多個包共用的上級目錄--> <context:component-scan base-package="controllerPackage"></context:component-scan> </beans>
(中央調度器 DispatcherServlet 負責創建 springmvc 容器 對象,讀取 spring 的 xml 配置文件,創建文件中的 Controller 對象。并且負責接收用戶的請求,分派給各個 Controller 對象。)
然后需要創建控制器,通過控制器來處理請求。比如下面我們創建了一個控制器類,通過注解 @RequestMapping 來將URL映射到處理方法中,即 /test.do 請求將會由 doTest() 方法來處理:
package controllerPackage; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class ControllerTest01 { /** *創建方法來處理請求,在springmvc中是通過方法來處理請求的。方法是自定義的,有多種返回值,多種參數 * @RequestMapping:請求映射,作用是把請求地址和方法綁定在一起。屬性的value可以是一個string,也可以是一個數組,不同方法間的值不能重復。 * @RequestMapping 可以放在方法上,也可以直接放在類上 */ @RequestMapping(value = "/test.do") public ModelAndView doTest() { //Spring MVC 通過 ModelAndView 對象把模型和視圖結合在一起 ModelAndView modelView = new ModelAndView(); //添加數據,框架最后會把數據放到request的作用域當中,類似于 request.setAttribute() modelView.addObject("name","張三"); modelView.addObject("age","22"); //指定視圖,指定視圖的完整路徑??蚣軙σ晥D執行forward操作,類似于request.getRequestDispatcher("/show.jsp").forward(); modelView.setViewName("/show.jsp"); return modelView; } }
在項目 src/main/webapp 目錄下創建 show.jsp,用來接收上面處理 /test.do 過后添加的數據:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>這里是show.jsp頁面</h1> <h2>hello:${name} ---- ${age}</h2> </body> </html>
然后我們就可以通過訪問 test.do 來進行測試了。啟動 tomcat,添加項目,訪問 test.do 請求。比如項目名為 springmvcProject,則訪問路徑為:http://localhost:8080/springmvcProject/test.do。最后可以看到結果如下:

2.2、配置視圖解析器(統一指定視圖的前綴和后綴)
上面的示例,我們把 show.jsp 建在了 webapp 目錄下,這樣的話用戶可以直接通過訪問 jsp 頁面的路徑來訪問頁面,而不是通過接口 .do 的形式來訪問,而不通過接口就無法拿到數據,可能會出現下面這種情況:

如果我們不希望用戶可以直接訪問到 jsp 頁面,我們可以把頁面文件建在 WEB-INF 目錄下,這樣用戶就無法通過輸入頁面路徑來直接訪問頁面,因為 WEB-INF 下的資源是無法通過瀏覽器直接訪問的。比如:

這樣我們指定視圖就可以寫成:
modelView.setViewName("/WEB-INF/view/show.jsp");
在指定視圖的時候,有可能有大量的重復路徑,例如:
mv.setViewName("/WEB-INF/view/show1.jsp");
mv.setViewName("/WEB-INF/view/show2.jsp");
mv.setViewName("/WEB-INF/view/show3.jsp");
我們可以在 spring 的配置文件中配置視圖解析器,可以指定視圖的前綴(路徑)和后綴(擴展名),讓框架來找到對應的視圖文件。例如,修改 springmvcTest-servlet.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--開啟組件掃描。base-package寫包名,若要掃描多個包,可以用逗號隔開,或者直接寫多個包共用的上級目錄--> <context:component-scan base-package="controllerPackage"></context:component-scan> <!-- springmvc框架中的視圖解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前綴:視圖文件的路徑--> <property name="prefix" value="/WEB-INF/view/"/> <!-- 后綴:視圖文件的擴展名--> <property name="suffix" value=".jsp"/> </bean> </beans>
在配置了視圖解析器后就可以直接用邏輯名稱(文件名)來指定視圖,框架會使用 “視圖解析器前綴+邏輯名稱+視圖解析器后綴” 來組成完整的路徑。
示例:
mv.setViewName("show1"); //相當于 mv.setViewName("/WEB-INF/view/show1.jsp");
mv.setViewName("show2");
mv.setViewName("show3");
2.3、解決訪問靜態資源報404的問題
中央調度器的 url-pattern 可以使用自定義擴展名,如:*.do、*.action、*.mvc等等,如<url-pattern>*.do</url-pattern>;也可以直接使用斜桿:<url-pattern>/</url-pattern>,表示攔截所有請求。當我們直接使用斜桿來攔截所有請求時,你會發現,在使用瀏覽器訪問靜態資源(比如html、css、img等)時會報 404。(在某些時候如果我們不希望通過 .do 來訪問后端接口,此時可以將 url-pattern 配置成 /,這樣就不需要給接口名后面添加 .do 后綴,直接寫即可,比如 @RequestMapping(value = "/test01"))
默認情況下,tomcat 會有一個 default 的 servlet(該 servlet 可以在 tomcat 安裝目錄下的 conf/web.xml 文件中找到),該 servlet 的 url-pattern 是 /,即攔截所有請求。當我們將中央調度器的 url-pattern 設置為 / 時,該中央調度器會替代 tomcat 的默認的 servlet,而因為中央調度器默認沒有處理靜態資源的能力,所以訪問靜態資源會報 404,但是訪問動態資源比如 xxx.do 能正常訪問。
所以當我們將中央調度器的 url-pattern 配置成 / 時,需要解決靜態資源訪問不到的問題。解決方法有以下兩種:
1)使用<mvc:default-servlet-handler />
在 spring 的配置文件(比如springmvcTest-servlet.xml)中配置<mvc:default-servlet-handler />,如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--開啟組件掃描。base-package寫包名,若要掃描多個包,可以用逗號隔開,或者直接寫多個包共用的上級目錄--> <context:component-scan base-package="controllerPackage"></context:component-scan> <!--注解驅動--> <mvc:annotation-driven/> <mvc:default-servlet-handler /> </beans>
配置<mvc:default-servlet-handler />后,Spring MVC上下文中會定義一個org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它會像一個檢查員,對進入DispatcherServlet的URL進行篩查,如果發現是靜態資源的請求,就將該請求轉由Web應用服務器默認的 Servlet 處理,如果不是靜態資源的請求,才由DispatcherServlet繼續處理。
在配置了 <mvc:default-servlet-handler /> 后,訪問 @RequestMapping 注解的方法可能報 404,這是因為 <mvc:default-servlet-handler /> 和 @RequestMapping 注解有沖突,此時我們在 spring 的配置文件上添加注解驅動即可。
2)使用<mvc:resources />
第一種方法即 <mvc:default-servlet-handler /> 是將靜態資源的處理 由Spring MVC 框架交回給了 tomcat 處理。而使用 <mvc:resources /> 則是由Spring MVC框架自己處理靜態資源,并可以添加一些有用的附加值功能。
在 spring 的配置文件下添加 <mvc:resources /> 標簽,示例:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--開啟組件掃描。base-package寫包名,若要掃描多個包,可以用逗號隔開,或者直接寫多個包共用的上級目錄--> <context:component-scan base-package="controllerPackage"></context:component-scan> <!--<mvc:resources /> 標簽 和 @RequestMapping 注解有沖突,所以需要添加注解驅動--> <mvc:annotation-driven/> <!-- mapping指的是瀏覽器訪問資源的路徑,比如http:localhost:8080/應用名/static/home.html locatioon指的是在服務器內,靜態資源存放的位置,比如下面的即表示靜態資源是在 webapp/static 目錄下 --> <mvc:resources mapping="/static/**" location="/static/" /> <!-- 可以定義多個mvc:resource標簽,為不同目錄下的靜態資源定義不同的mapping和location,但是建議將靜態資源都放在同一個文件夾下,比如 webapp/static 下,這樣就可以只保留上面一個標簽--> <mvc:resources mapping="/css/**" location="/css/" /> </beans>
使用 <mvc:resources /> 允許靜態資源放在任何地方,如 WEB-INF目錄下、類路徑下等。通過 location屬性指定靜態資源的位置,由于location屬性是Resources類型,因此可以使用諸如"classpath:"等的資源前綴指定資源位置。傳統Web容器的靜態資源只能放在Web容器的根路徑下,<mvc:resources />完全打破了這個限制。
其次,<mvc:resources />依據當前著名的Page Speed、YSlow等瀏覽器優化原則對靜態資源提供優化。你可以通過cacheSeconds屬性指定靜態資源在瀏覽器端的緩存時間,一般可將該時間設置為一年,以充分利用瀏覽器端的緩存。在輸出靜態資源時,會根據配置設置好響應報文頭的Expires 和 Cache-Control值。在接收到靜態資源的獲取請求時,會檢查請求頭的Last-Modified值,如果靜態資源沒有發生變化,則直接返回303相應狀態碼,提示客戶端使用瀏覽器緩存的數據,而非將靜態資源的內容輸出到客戶端,以充分節省帶寬,提高程序性能。
3、@RequestMapping注解
@RequestMapping 將請求地址和方法綁定在一起。屬性的value可以是一個string,也可以是一個數組,不同方法間的值不能重復。
3.1、@RequestMapping作用在類上
@RequestMapping 既可以作用在方法上,也可以作用在類上。當@RequestMapping 標記在Controller 類上的時候,類里面使用@RequestMapping 標記的方法的請求地址都是相對于類上的@RequestMapping 而言的。簡單來說,就是給類上的方法統一加了一個前綴。
示例:
package controllerPackage; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/user") public class ControllerTest02 { @RequestMapping(value = "/test1.do") public ModelAndView doTest() { ... } @RequestMapping(value = "/test2.do") public ModelAndView doTest2() { ... } }
此時訪問 test1.do 或者 test2.do 實際上應該訪問的路徑為:localhost:8080/項目名/user/test1.do。注意,需要在前面加上 /user。
3.2、method 屬性
@RequestMapping 的 method 屬性可以指定方法只能被指定的請求方式訪問,如果不指定請求方式時,則表示不限制請求方式,即任何方式都行。該屬性的值是 RequestMethod 類枚舉值,比如:RequestMethod.GET、RequestMethod.POST 等等。
示例:
package controllerPackage; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/user") public class ControllerTest02 { @RequestMapping(value = "/test1.do", method = RequestMethod.GET) public ModelAndView doTest() { ... } @RequestMapping(value = "/test2.do", method = {RequestMethod.GET, RequestMethod.DELETE}) public ModelAndView doTest2() { ... } }
指定了請求方式后,如果使用非指定的請求方式來訪問接口的話,瀏覽器會報 405,提示該請求方式不被支持。
4、處理器方法接收前端請求的參數
4.1、通過HttpServletRequest對象獲取參數
可以給 @RequestMapping 注解的方法添加 HttpServletRequest 對象的屬性,通過該對象可以獲取到前端請求的參數:
@Controller public class ControllerTest03 { @RequestMapping(value = "/test01.do") public ModelAndView doTest(HttpServletRequest request, HttpServletResponse response, HttpSession httpSessiont) { System.out.println(request.getParameter("name")); // 獲取到前端發送的name屬性值 //... } }
我們可以通過訪問類似 http://localhost:8080/springmvcProject/test01.do?name=zhangsan 的地址給后臺接口發送數據,后臺接口即可接收到 name 屬性的值。
4.2、通過同名形參直接獲取參數
可以給 @RequestMapping 注解的方法添加跟前端發送的參數同名的形參,即可通過形參來直接獲取前端發送的同名參數:
@Controller public class ControllerTest03 { @RequestMapping(value = "/test01.do") public ModelAndView doTest(String name, int age) { System.out.println(name + "----" + age); //直接獲取到前端發送的name和age參數值 //... } }
我們可以通過訪問類似 http://localhost:8080/springmvcProject/test01.do?name=zhangsan&age=22 的地址給后臺接口發送數據,后臺接口即可接收到對應的參數值。
這種通過同名形參直接獲取前端同名參數的方法,實際上是 springmvc 框架通過 HttpServletRequest 對象的 getParameter() 方法來將前端參數賦值給對應的形參的。并且框架同時還提供類型轉換的功能,例如上面,會先將 age 的值轉換為 int 類型,然后再賦值給形參 age。
4.3、形參和實參不同名(@RequestParam())
通過與參數同名的形參能夠直接獲取前端參數,但如果不同名,我們也可以通過 @RequestParam() 注解來指定參數的別名來獲取參數。
@Controller public class ControllerTest02 { @RequestMapping(value = "/test3.do") public ModelAndView doTest2(@RequestParam(name = "othername") String name) { System.out.println(name); //... } }
如上,前端請求的參數可以是 othername。通過 @RequestParam(name = "othername") 和 @RequestParam(value = "othername") 或者不加屬性值即 @RequestParam("othername") 設置都一樣。
@RequestParam() 還可以設置屬性值 required 為 false,即 @RequestParam(required = false),此時前端可以不用傳該參數 java 程序也不會報錯。
4.4、通過對象來獲取參數
如果參數過多,一個個寫參數可能比較麻煩,此時我們可以定義一個 Java 實體類,該類的屬性名跟前端參數名對應,通過類的對象作為形參來獲取參數。
示例:
定義一個實體類:
public class Person { String name; int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
將該類的對象作為形參,springmvc 框架會自動創建形參中類的對象,并且調用該對象的 setter 方法給屬性賦值:
@Controller public class ControllerTest02 { @RequestMapping(value = "/test4.do") public ModelAndView doTest4(Person person) { System.out.println(person.getName()); //... } }
4.5、配置字符編碼過濾器(解決中文亂碼問題)
在獲取前端中文參數時,可能會出現亂碼問題。如果使用 HttpServletResponse 對象來返回數據,可以使用 resp.setContentType("text/html;charset=utf-8"); 來解決中文亂碼問題,但需要在每個請求里都設置,比較麻煩。
我們可以在 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"> ... <!-- characterEncodingFilter字符編碼過濾器 --> <filter> <filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param> <!--要使用的字符集,一般使用UTF-8--> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param>
<init-param> <!--是否強制設置request的編碼為encoding,默認為false--> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param>
<init-param> <!--是否強制設置response的編碼為encoding--> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter>
<filter-mapping> <filter-name>characterEncodingFilter</filter-name> <!--這里不能留空或者直接寫'/',否則不起作用--> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
上面設置編碼過后,返回給前端的數據編碼將會是 utf-8,解決中文亂碼問題。
但是設置了上面字符編碼過濾器后,如果直接訪問服務器中的 html 頁面,你會發現 html 頁面里的中文變亂碼了。此時將 forceRequestEncoding 和 forceResponseEncoding 去掉即可。
5、響應請求返回數據
5.1、返回ModelAndView類型(響應一個jsp頁面)
若處理器方法處理完,需要跳轉其它 jsp 資源,并且需要傳遞數據,則可以返回 ModelAndView 。此時JSP頁面可以使用 JSTL 標簽來匹配解析后端返回的數據。
示例:
package controllerPackage; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class ControllerTest01 { @RequestMapping(value = "/test.do") public ModelAndView doTest() { ModelAndView modelView = new ModelAndView(); modelView.addObject("name","張三"); modelView.addObject("age","22"); modelView.setViewName("/show.jsp"); return modelView; } }
5.2、返回String(響應一個jsp頁面)
處理器方法可以返回字符串,該字符串可以是邏輯名稱(文件名稱),也可以是完整的視圖路徑。最終該處理方法會跳轉到該字符串所指定的資源。
示例:
package controllerPackage; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class ControllerTest02 { @RequestMapping(value = "/returnStringTest.do") public String doTest() { //框架實際上是對視圖執行forward操作 return "/WEB-INF/view/show1.jsp"; //或者如果我們配置了視圖解析器,則應該直接寫邏輯名稱,比如:show1 } }
5.3、返回值為void(響應ajax請求)
在返回值為 void 時,我們可以用來響應 ajax 請求。
package controllerPackage; import com.alibaba.fastjson.JSON; ... @Controller public class ControllerTest02 { //直接給 ajax 請求返回字符串 @RequestMapping("/ajaxResponse.do") public void ajaxResponse(HttpServletRequest request, HttpServletResponse response) throws Exception{ PrintWriter out = response.getWriter(); out.write("hello"); } //給 ajax 請求返回一個json格式字符串 @RequestMapping("/ajaxJson.do") public void ajaxJson(HttpServletRequest request, HttpServletResponse response) throws Exception{ PrintWriter out = response.getWriter(); //通過 com.alibaba.fastjson.JSON 包來將對象轉換成json格式字符串 Student student = new Student(1, "wen"); String stuStr = JSON.toJSONString(student); out.write(stuStr); } }
5.4、返回Object類型(包括Integer、String、Map、List、自定義對象等等,響應ajax請求)
處理器也可以返回 Object 對象,這個對象可以是 Integer、String、Map、List、自定義對象等等。但返回的對象不是作為邏輯視圖出現的,而是直接給前端請求返回數據的。
首先需要先引入處理 json 的工具庫,springmvc 中默認使用的是 jackson:
<?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>springmvc_maven_test01</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.4</version> </dependency> </dependencies> ... </project>
然后給 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" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--開啟組件掃描。base-package寫包名,若要掃描多個包,可以用逗號隔開,或者直接寫多個包共用的上級目錄--> <context:component-scan base-package="controllerPackage"></context:component-scan> <!--注解驅動--> <mvc:annotation-driven/> </beans>
添加注解驅動時需要注意是添加 mvc 的,不要添加成其他同名的。

注解驅動的作用是將 java 對象轉換為 json、xml、text、二進制等格式數據,框架會自動識別 java 對象能轉換為什么數據格式,然后進行返回。<mvc:annotation-driven/> 在加入到配置文件后,會自動創建 HttpMessageConverter 接口的實現類對象,這個接口的實現類完成了 java 對象到其他數據格式的轉換功能。
在使用時需要在方法前添加 @ResponseBody 注解,springmvc 通過該注解將會把處理器方法已經轉換后生成的數據通過 HttpServletResponse 返回給前端的 ajax 請求。
代碼示例:
package controllerPackage; import entity.Student; ... @Controller public class ControllerTest03 { @RequestMapping("/objJson.do") @ResponseBody //把處理器方法返回的對象轉換為json后,通過HttpServletResponse返回給前端 public Student objJson(HttpServletRequest request, HttpServletResponse response){ Student student = new Student(1, "wen"); return student; } }
返回一個類對象,前端接收到的字符串轉換為 json 格式后將會是一個對象 obj。
如果返回的是一個 Map,則前端接收到的字符串經轉換為 json 后也是一個對象 obj;如果返回的是一個 list,則前端接收到的字符串經轉換為 json 后將會是一個數組,并且順序跟 list 添加元素的順序一致;如果是一個字符串 String,前端接收到的也是字符串。
代碼示例:
package controllerPackage; import entity.Student; ... @Controller public class ControllerTest03 { //返回一個list @RequestMapping("/objList.do") @ResponseBody //把處理器方法返回的對象轉換為json后,通過HttpServletResponse返回給前端 public List<Student> objList(HttpServletRequest request, HttpServletResponse response){ List<Student> list = new ArrayList<>(); Student student1 = new Student(1, "wen"); Student student2 = new Student(2, "wen2"); list.add(student1); list.add(student2); return list; } //返回一個map @RequestMapping("/objMap.do") @ResponseBody //把處理器方法返回的對象轉換為json后,通過HttpServletResponse返回給前端 public Map<String, String> objMap(HttpServletRequest request, HttpServletResponse response){ Map<String, String> testMap = new HashMap<>(); testMap.put("hello", "你好"); testMap.put("world", "世界"); return testMap; } //直接返回字符串 @RequestMapping("/objString.do") @ResponseBody //把處理器方法返回的對象轉換為json后,通過HttpServletResponse返回給前端 public String objString(HttpServletRequest request, HttpServletResponse response){ return "你好啊hello~"; } }
5.4.1、解決返回 String 時中文亂碼問題
使用上述方法來返回 String 時,如果字符串中有中文,則前端接收到的數據可能會有中文亂碼問題(注意,此時返回字符串是直接返回數據,而不是返回視圖 jsp 資源)。出現中文亂碼問題這是默認返回的響應頭 content-type 中指定的編碼格式是 ISO-8859-1,如下:

解決中文亂碼問題我們可以通過 @RequestMapping 注解的 produces 屬性來指定響應頭的 content-type,如下:
@Controller public class ControllerTest03 { @RequestMapping(value = "/objString.do", produces = "text/html;charset=utf-8") //produces相當于給response指定了Content-Type響應頭 @ResponseBody public String objString(HttpServletRequest request, HttpServletResponse response){ return "你好啊hello~"; } }
6、Springmvc接收各種參數(字符串、json)
6.1、接收普通參數
前端發送請求方法:
$.ajax({
url: 'getStr.do',
type: 'post', //此時get請求和post請求發送參數的寫法一樣
data: {
name: 'wen',
age: 11
},
success: function (data) {
console.log('后端返回數據', data); //這里將接收到后端返回的字符串
}
})
springmvc 代碼,請求參數名跟Controller的方法參數一致:
@Controller public class ControllerTest01 { @RequestMapping(value = "/getStr.do", produces = "text/html;charset=utf-8") @ResponseBody public String doTest(String name, int age) { System.out.println(name + "----" + age); //直接獲取到前端發送的name和age參數值 return "你好啊hello~"; } }
此時可以看到前端發送的 url、響應頭 content-type、發送頭 content-type 和參數如下:




6.2、將對象作為參數
當請求參數過多時,后端可以以對象作為參數接收前端發送的數據,而此時前端發送數據跟發送普通參數一樣。
前端代碼如下,此時發送數據跟發送普通參數一致:
$.ajax({
url: 'getObj.do',
type: 'post',
data: {
name: 'wen',
age: 11
},
success: function (data) {
console.log('后端返回數據', data)
}
})
實體類如下,實體類可以寫構造函數也可以不寫。
package domain; public class Student { String name; int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(String name, int age) { System.out.println(11112222); this.name = name; this.age = age; } }
controller 類:
@Controller public class ControllerTest01 { @RequestMapping(value = "/getObj.do") //此時不能寫produces = "text/html;charset=utf-8" 因為此時返回的是對象,默認將是json格式,如果寫成text/html將導致接口有問題 @ResponseBody public Student doTest02(Student student) { System.out.println(student.getName() + "----" + student.getAge()); return student; } }
此時可以看到前端發送的 url、響應頭 content-type、發送頭 content-type 和參數如下:




6.3、接收復雜對象
如果接收的是復雜對象,比如對象當中還嵌套著對象。此時前端發送請求時需指定 content-type 為 'application/json;charset=utf-8',并且后端需要用 @RequestBody 進行接收。
前端發送參數格式如下:
$.ajax({ url: 'getComplexObj.do', contentType: 'application/json;charset=utf-8', type: 'post', data: JSON.stringify({ complexName: '復雜對象名', //這里也可以是一個對象,這樣的話此時后端的實體類中該字段也應該是一個實體類 student: { name: '學生名', age: 111 } }), success: function (data) { console.log('后端返回數據', data) } })
復雜對象類:
package domain; public class ComplexDomain { private Student student; //注意,此時student類中不能有自定義構造函數 private String complexName; public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } public String getComplexName() { return complexName; } public void setComplexName(String complexName) { this.complexName = complexName; } }
controller類:
@Controller public class ControllerTest01 { @RequestMapping(value = "/getComplexObj.do") @ResponseBody public ComplexDomain doTest03(@RequestBody ComplexDomain complexDomain) { System.out.println(complexDomain.getComplexName() + "----" + complexDomain.getStudent().getName() + "----" + complexDomain.getStudent().getAge()); return complexDomain; } }
此時可以看到前端發送的 url、響應頭 content-type、發送頭 content-type 和參數如下:




浙公網安備 33010602011771號