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 包文件。

搭建好的項目工程整體目錄比較簡單,具體如下圖所示:

image

項目工程結構簡單介紹:

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
@ComponentScan("com.jobs")
//啟用 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




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM