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 @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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性