SpringMvc 資料和頁面響應 Response 總結
上一篇部落格已經介紹了 SpringMvc 純註解搭建,以及常用的請求獲取引數的幾種方式。為了保持完整性,本篇部落格仍然會列出 SpringMvc 的純註解搭建過程,另外會對比上一篇部落格,對一些新增的註解配置項進行介紹,然後主要通過程式碼演示的方式,介紹 SpringMvc 響應 Response 的一些常用方式。
由於目前在網站開發領域,漸漸的都採用前後端分離的技術,很少再有伺服器端生成網頁,因此大家重點掌握 SpringMvc 編寫介面返回 Json 資料的響應方式,其它的響應方式僅僅瞭解即可,如果後續用到了,只需要在本部落格檢視一下即可。在本篇部落格的最後,會提供 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> <!--匯入 jackson 相關的 jar 包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> </dependencies>
這次相比於上一篇部落格,多引入了 jackson 的 jar 包,主要是因為 SpringMvc 預設採用 jackson 元件來處理 Json。由於 Json 資料是當前 web 開發中最流行的資料傳輸方式,即使我們不使用 SpringMvc 的自動處理 Json 功能,我們也得自己使用 jackson 元件生成 json 資料返回。
配置好引用的 jar 包後,開啟右側的 Maven 視窗,重新整理一下,這樣 Maven 會自動下載所需的 jar 包檔案。
搭建好的專案工程整體目錄比較簡單,具體如下圖所示:
專案工程結構簡單介紹:
com.jobs.config 包下儲存的是 SpringMvc 的配置檔案和 Servlet 的初始化檔案
com.jobs.controller 包下儲存的是用於提供 http 請求和響應的類
com.jobs.domain 包下儲存的是 JavaBean 實體類
web 目錄下放置的是網站檔案
二、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.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
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;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
//讓 SpringMvc 僅僅掃描載入配置了 @Controller 註解的類
@ComponentScan(value = "com.jobs",
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class}))
//啟用 mvc 功能,配置了該註解之後,SpringMvc 攔截器放行相關資源的設定,才會生效
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
//配置 SpringMvc 聯結器放行常用資源的格式(圖片,js,css)
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置響應資料格式所對應的資料處理轉換器
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//如果響應的是 application/json ,則使用 jackson 轉換器進行自動處理
MappingJackson2HttpMessageConverter jsonConverter =
new MappingJackson2HttpMessageConverter();
jsonConverter.setDefaultCharset(Charset.forName("UTF-8"));
List<MediaType> typelist1 = new ArrayList<>();
typelist1.add(MediaType.APPLICATION_JSON);
jsonConverter.setSupportedMediaTypes(typelist1);
converters.add(jsonConverter);
//如果響應的是 text/html 和 text/plain ,則使用字串文字轉換器自動處理
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setDefaultCharset(Charset.forName("UTF-8"));
List<MediaType> typelist2 = new ArrayList<>();
typelist2.add(MediaType.TEXT_HTML);
typelist2.add(MediaType.TEXT_PLAIN);
stringConverter.setSupportedMediaTypes(typelist2);
converters.add(stringConverter);
}
//註解配置 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;
}
}
相比於上一篇部落格,這裡面多了一個重寫方法:configureMessageConverters 。重寫這個方法主要是為了讓 SpringMvc 根據響應的資料格式,採用不同的格式轉換器自動處理。比如上面的程式碼,當遇到 application/json 的響應格式時,自動採用 jackson 元件進行轉換處理,當遇到 text/plain 和 text/html 時,自動採用字串的方式進行處理。重寫了 configureMessageConverters 後,在 SpringMvc 的配置類上啟用註解 @EnableWebMvc 即可。
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();
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, "/*");
}
}
三、常見的響應方式
還是首選介紹一下 domian 下的 Employee 實體類,相比於上一篇部落格,多了建構函式,其它沒啥變化:
package com.jobs.domain;
import java.io.Serializable;
import java.util.List;
public class Employee implements Serializable {
//姓名
private String name;
//年齡
private Integer age;
//愛好
private List<String> hobby;
public Employee() {
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
//這裡剩餘了相關欄位的 get 和 set 方法...
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", hobby=" + hobby +
'}';
}
}
先列出 RespController1 的內容,主要是通過在方法上增加註解 @ResponseBody 表示處理的結果不再是轉發到相關頁面,而是直接返回資料內容,這種方式非常適合前後端分離的開發方式,用於給前端頁面提供資料介面,具體內容如下:
package com.jobs.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jobs.domain.Employee;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequestMapping("resp1")
@Controller
public class RespController1 {
//使用 Servlet 的原生 response 響應資料
@RequestMapping(value = "/test1")
public void respTest1(HttpServletResponse response) throws IOException {
//原生的響應,需要自己設定資料響應格式
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("原生 Response 響應資訊");
}
//為了讓 SpringMvc 自動根據響應的資料格式,進行資料處理,並返回
//需要滿足 2 個條件:
//1 在 SpringMvcConfig 這個配置類上,啟用 @EnableWebMvc 註解
//2 重寫 configureMessageConverters 方法,新增不同響應格式的轉換器
//本 demo 僅僅使用了 3 種響應格式:text/html,text/plain,application/json
//使用 @ResponseBody 註解可以將返回的結果直接作為響應內容,不需要返回具體的頁面
//produces 表示響應的資料格式
@RequestMapping(value = "/test2", produces = "text/plain")
@ResponseBody
public String respTest2() {
String result = "努力學習,天天向上";
return result;
}
//通過第三方的 jackson 元件生成 json 字串並返回
@RequestMapping(value = "/test3", produces = "text/html")
@ResponseBody
public String respTest3() throws Exception {
Employee emp = new Employee("喬豆豆", 38);
emp.setHobby(new ArrayList<String>(List.of("看書", "學習", "唱歌")));
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(emp);
}
@RequestMapping(value = "/test4", produces = "application/json")
@ResponseBody
public Employee respTest4() {
Employee emp = new Employee("喬豆豆", 38);
emp.setHobby(new ArrayList<String>(List.of("看書", "學習", "唱歌")));
return emp;
}
@RequestMapping(value = "/test5", produces = "application/json")
@ResponseBody
public List respTest5() {
List<Employee> list = new ArrayList<>();
Employee emp1 = new Employee("任肥肥", 40);
emp1.setHobby(new ArrayList<String>(List.of("美食", "抽菸", "喝酒")));
list.add(emp1);
Employee emp2 = new Employee("候胖胖", 42);
emp2.setHobby(new ArrayList<String>(List.of("飆車", "運動", "搞笑")));
list.add(emp2);
return list;
}
@RequestMapping(value = "/test6", produces = "application/json")
@ResponseBody
public Map respTest6() {
Map<String, String> map = new HashMap<>();
map.put("name", "喬豆豆");
map.put("age", "38");
map.put("gender", "男");
return map;
}
}
如果你只是想寫介面返回資料,不想在每個方法上使用 @ResponseBody 註解,可以直接在類上使用 @RestController 註解。@RestController 註解已經包含了 @ResponseBody 註解的功能,因此類中的每個方法不需要再使用 @ResponseBody 註解,具體內容如下:
package com.jobs.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jobs.domain.Employee;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//如果你只想寫介面,返回資料,不想每個方法上新增 @ResponseBody
//那麼可以直接在類上增加註解 @RestController 即可
@RequestMapping("resp2")
@RestController
public class RespController2 {
//SpringMvc 預設情況下,各種 http 請求方式都支援,
//如果想僅支援一部分的話,可以通過 method 進行配置
@RequestMapping(value = "/test1", method = RequestMethod.GET)
public void respTest1(HttpServletResponse response) throws Exception {
//原生的響應,需要自己設定資料響應格式
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("原生 Response 響應資訊");
}
//為了讓 SpringMvc 自動根據響應的資料格式,進行資料處理,並返回
//需要滿足 2 個條件:
//1 在 SpringMvcConfig 這個配置類上,啟用 @EnableWebMvc 註解
//2 重寫 configureMessageConverters 方法,新增不同響應格式的轉換器
//本 demo 僅僅使用了 3 種響應格式:text/html,text/plain,application/json
@RequestMapping(value = "/test2", produces = "text/plain")
public String respTest2() throws Exception {
String result = "努力學習,天天向上";
return result;
}
@RequestMapping(value = "/test3", produces = "text/html")
public String respTest3() throws Exception {
Employee emp = new Employee("喬豆豆", 38);
emp.setHobby(new ArrayList<String>(List.of("看書", "學習", "唱歌")));
ObjectMapper om = new ObjectMapper();
String result = om.writeValueAsString(emp);
return result;
}
@RequestMapping(value = "/test4", produces = "application/json")
@ResponseBody
public Employee respTest4() {
Employee emp = new Employee("喬豆豆", 38);
emp.setHobby(new ArrayList<String>(List.of("看書", "學習", "唱歌")));
return emp;
}
@RequestMapping(value = "/test5", produces = "application/json")
public List respTest5() {
List<Employee> list = new ArrayList<>();
Employee emp1 = new Employee("任肥肥", 40);
emp1.setHobby(new ArrayList<String>(List.of("美食", "抽菸", "喝酒")));
list.add(emp1);
Employee emp2 = new Employee("候胖胖", 42);
emp2.setHobby(new ArrayList<String>(List.of("飆車", "運動", "搞笑")));
list.add(emp2);
return list;
}
@RequestMapping(value = "/test6", produces = "application/json")
public Map respTest6() {
Map<String, String> map = new HashMap<>();
map.put("name", "喬豆豆");
map.put("age", "38");
map.put("gender", "男");
return map;
}
}
如果類上沒有 @RestController 註解,且類中的方法上也沒有 @ResponseBody 註解,那麼方法的返回值如果是字串的話,就表示要轉發的目標頁面。這是存在多種頁面轉發和頁面跳轉的方式,具體內容如下:
package com.jobs.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/resp3")
@Controller
public class RespController3 {
@RequestMapping("/test1")
public String respTest1() {
//由於在 SpringMvcConfig 配置類中的 getViewResolver 方法中配置了字首和字尾
//所以如果返回字串 success ,會去 /WEB-INF/pages 目錄下找 success.jsp 頁面
return "success";
}
@RequestMapping("/test2")
public String respTest2() {
//由於在 SpringMvcConfig 配置類中的 getViewResolver 方法中配置了字首和字尾
//所以如果返回字串 /test/test 的話,(注意:/ 代表 /WEB-INF/pages 目錄)
//會去 /WEB-INF/pages 目錄下找 test 目錄下的 test.jsp 頁面
return "/test/test";
}
@RequestMapping("/test3")
public String respTest3() {
//如果不想使用 getViewResolver 方法中配置的字首和字尾,可以註釋了
//如果只是臨時不想用的話,可以使用 forward 和 redirect 跳轉
//此時 / 表示 web 目錄,需要自己寫出完整的頁面路徑
return "forward:/WEB-INF/pages/success.jsp";
}
@RequestMapping("/test4")
public String respTest4() {
//如果不想使用 getViewResolver 方法中配置的字首和字尾,可以註釋了
//如果只是臨時不想用的話,可以使用 forward 和 redirect 跳轉
//此時 / 表示 web 目錄,需要自己寫出完整的頁面路徑
//需要注意:WEB-INF 目錄只能通過程式內部轉發訪問,瀏覽器無法直接訪問
//因此 redirect 重定向無法訪問 WEB-INF 目錄。這裡訪問 web 下的 ok.html 頁面
return "redirect:/ok.html";
}
}
最後我們演示一下,如果將相關的資料,傳遞到轉發的目標 jsp 頁面上並使用資料,具體內容如下:
package com.jobs.controller;
import com.jobs.domain.Employee;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
@RequestMapping("/resp4")
@Controller
public class RespController4 {
//這裡主要演示從 Controller 向轉發的目標 jsp 攜帶和傳送資料
//使用 Servlet 的原生 request 物件攜帶資料,傳遞到 data.jsp 中
@RequestMapping("/test1")
public String respTest1(HttpServletRequest request) {
//給 Attribute 設定資料,傳送給 data.jsp 頁面
request.setAttribute("data","事業有成");
return "data";
}
//通過 Model 形參傳遞資料給 data.jsp 頁面
@RequestMapping("/test2")
public String respTest2(Model model) {
model.addAttribute("data","通過 model 傳遞資料");
Employee emp = new Employee("喬豆豆", 38);
emp.setHobby(new ArrayList<String>(List.of("看書", "學習", "唱歌")));
model.addAttribute("emp",emp);
return "data";
}
//通過 modelAndView 形參傳遞資料給 data.jsp 頁面
@RequestMapping("/test3")
public ModelAndView respTest3(ModelAndView modelAndView) {
//新增資料
modelAndView.addObject("data","通過 ModelAndView 傳遞資料");
//新增資料
Employee emp = new Employee("任肥肥", 38);
emp.setHobby(new ArrayList<String>(List.of("抽菸", "喝酒", "摸魚")));
modelAndView.addObject("emp",emp);
//新增要跳轉的頁面
modelAndView.setViewName("data");
return modelAndView;
}
//ModelAndView 物件也支援 forward 轉發,不使用配置的字首和字尾
@RequestMapping("/test4")
public ModelAndView respTest4(ModelAndView modelAndView)
{
modelAndView.setViewName("forward:/WEB-INF/pages/test/test.jsp");
return modelAndView;
}
//ModelAndView 物件也支援 redirect 重定向,不使用配置的字首和字尾
@RequestMapping("/test5")
public ModelAndView respTest5(ModelAndView modelAndView)
{
modelAndView.setViewName("redirect:/ok.html");
return modelAndView;
}
}
上面程式碼中的 data.jsp 頁面,就是使用資料的頁面,其頁面程式碼具體內容如下:
<%@ page import="com.jobs.domain.Employee" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>接收資料測試</title>
</head>
<body>
<h1>這裡是 data.jsp 頁面,接收到的資料如下:</h1>
接收到的 data 資料值為:${data} <br/>
員工的姓名為:${emp.name}<br/>
員工的年齡為:${emp.age}<br/>
員工的愛好為:${emp.hobby}
</body>
</html>
到此為止,已經詳細列出了 SpringMvc 常用的幾種響應資料的方式,總體使用起來非常簡單方便。
本篇部落格的 demo 原始碼下載地址為:https://files.cnblogs.com/files/blogs/699532/SpringMvc_Response.zip