前言
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过一套注解,我们可以快速的搭建一个Web应用。在本文中,我们将重点介绍如何在Spring MVC中返回JSON数据以及如何处理异常。
一、JSON数据返回
1.导入依赖
为了使@ResponseBody注解生效,我们需要配置一个Jackson消息转换器,用于将Java对象序列化为JSON字符串。需要在pom.xml文件中添加Jackson依赖:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.3</version> </dependency>
2.配置spring-mvc.xml
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJackson2HttpMessageConverter"/> </list> </property> </bean> <bean id="mappingJackson2HttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <!--处理中文乱码以及避免IE执行AJAX时,返回JSON出现下载文件--> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>text/json;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> </list> </property> </bean>
3.使用@ResponseBody注解
在SpringMVC中,我们可以使用@ResponseBody注解将Controller方法的返回值直接转换为JSON格式。这样,当客户端请求该方法时,将会收到一个JSON格式的响应。
package com.ctb.controller; import com.ctb.biz.UserBiz; import com.ctb.model.User; import com.ctb.utils.PageBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.List; import java.util.Map; @Controller @RequestMapping("/user/json") public class JsonController { @Autowired private UserBiz userBiz; /** * 返回List<T> JSON数组 * @param req * @param user * @return */ @ResponseBody @RequestMapping("/list") public List<User> list(HttpServletRequest req, User user){ PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<User> lst = this.userBiz.listPager(user, pageBean); System.out.println(1 / 0); return lst; } /** * 返回T JSON对象 * @param req * @param user * @return */ @ResponseBody @RequestMapping("/load") public User load(HttpServletRequest req, User user){ if(user.getId() != null){ List<User> lst = this.userBiz.listPager(user, null); return lst.get(0); } return null; } /** * 返回List<Map> JSON数组 * @param req * @param user * @return */ @ResponseBody @RequestMapping("/mapList") public List<Map> mapList(HttpServletRequest req, User user){ PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Map> lst = this.userBiz.mapListPager(user, pageBean); return lst; } /** * 返回Map JSON对象 * @param req * @param user * @return */ @ResponseBody @RequestMapping("/mapLoad") public Map mapLoad(HttpServletRequest req, User user){ if(user.getId() != null){ List<Map> lst = this.userBiz.mapListPager(user, null); return lst.get(0); } return null; } //混合 @ResponseBody @RequestMapping("/all") public Map all(HttpServletRequest req, User user){ PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<User> lst = this.userBiz.listPager(user, pageBean); Map map = new HashMap(); map.put("lst",lst); map.put("pageBean",pageBean); return map; } @ResponseBody// @RequestMapping("/jsonStr") public String jsonStr(HttpServletRequest req, User user){ return "userEdit"; } }
注意:在使用此注解之后不会再走视图解析器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。分别为对象,数组,对象数组(混合)
以下是部分方法运行结果
4.Jackson
4.1.介绍
Jackson是一个简单基于Java应用库,Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。Jackson所依赖的jar包较少,简单易用并且性能也要相对高些,并且Jackson社区相对比较活跃,更新速度也比较快。
特点
- 容易使用,提供了高层次外观,简化常用的用例。
- 无需创建映射,API提供了默认的映射大部分对象序列化。
- 性能高,快速,低内存占用
- 创建干净的json
- 不依赖其他库
- 代码开源
4.2.常用注解
注解 | 说明 |
@JsonIgnore | 作用在字段或方法上,用来完全忽略被注解的字段和方法对应的属性 |
@JsonProperty | 作用在字段或方法上,用来对属性的序列化/反序列化,可以用来避免遗漏属性,同时提供对属性名称重命名 |
@JsonIgnoreProperties | 作用在类上,用来说明有些属性在序列化/反序列化时需要忽略掉 |
@JsonUnwrapped | 作用在属性字段或方法上,用来将子JSON对象的属性添加到封闭的JSON对象 |
@JsonFormat | 指定序列化日期/时间值时的格式 |
1.为什么要全局异常处理
我们知道,系统中异常包括:编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。
2.异常处理思路
系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
3.SpringMVC异常分类
- 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;
- 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器;
- 使用@ControllerAdvice + @ExceptionHandler
4.综合案例
4.1.异常处理方式一
SpringMVC中自带了一个异常处理器叫SimpleMappingExceptionResolver,该处理器实现了HandlerExceptionResolver 接口,全局异常处理器都需要实现该接口。
<!-- springmvc提供的简单异常处理器 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 定义默认的异常处理页面 --> <property name="defaultErrorView" value="error"/> <!-- 定义异常处理页面用来获取异常信息的变量名,也可不定义,默认名为exception --> <property name="exceptionAttribute" value="ex"/> <!-- 定义需要特殊处理的异常,这是重要点 --> <property name="exceptionMappings"> <props> <prop key="java.lang.RuntimeException">error</prop> </props> <!-- 还可以定义其他的自定义异常 --> </property> </bean>
注:页面跳转由SpringMVC来接管了,所以此处的定义默认的异常处理页面都应该配置成逻辑视图名。
前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>错误界面</title> </head> <body> 错误信息界面。。。。。。 ${ex} </body> </html>
4.2.异常处理方式二
- 创建一个名为GlobalException的自定义异常类
这是一个名为GlobalException
的Java类,它继承自RuntimeException
。RuntimeException
是Java中所有未检查异常的父类。这个类提供了多种构造方法,用于创建不同类型的异常对象。
package com.ctb.exception; public class GlobalException extends RuntimeException { // 默认构造方法,创建一个不带任何消息和原因的运行时异常 public GlobalException() { } // 带一个字符串消息的构造方法,创建一个带有指定消息的运行时异常 public GlobalException(String message) { super(message); } // 带一个字符串消息和一个原因的构造方法,创建一个带有指定消息和原因的运行时异常 public GlobalException(String message, Throwable cause) { super(message, cause); } // 带一个原因的构造方法,创建一个带有指定原因的运行时异常 public GlobalException(Throwable cause) { super(cause); } // 带一个字符串消息、一个原因、一个标志位(是否抑制异常)和一个标志位(是否可写栈轨迹)的构造方法,创建一个带有指定消息、原因、抑制标志位和可写栈轨迹的运行时异常 public GlobalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
- 创建一个名为GlobalExceptionHandler的类,并实现了HandlerExceptionResolver接口。
package com.ctb.component; import com.ctb.exception.GlobalException; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class GlobalExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mv = new ModelAndView(); mv.setViewName("error");//error.jsp if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; mv.addObject("ex",globalException.getMessage()); mv.addObject("msg","全局异常...."); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; mv.addObject("ex",runtimeException.getMessage()); mv.addObject("msg","运行时异常...."); } return mv; } }
它实现了HandlerExceptionResolver接口(异常处理解析器)。当应用程序中抛出异常时,这个处理器会被调用,返回一个ModelAndView对象,用于渲染错误页面。
代码解析:
@Component
注解表示这个类是一个Spring组件,Spring会自动扫描并管理这个类的实例。GlobalExceptionHandler
类实现了HandlerExceptionResolver
接口,该接口只有一个方法resolveException
,Spring在处理异常时会调用这个方法。resolveException
方法接收四个参数,分别是HttpServletRequest、HttpServletResponse、Object和Exception。HttpServletRequest和HttpServletResponse是HTTP请求和响应的对象,Object是当前处理的请求或响应的对象,Exception是发生的异常。- 在
resolveException
方法中,首先创建了一个ModelAndView对象mv,然后设置了视图名称为"error",即错误页面。- 接着判断异常的类型,如果是GlobalException类型,则获取其消息并添加到mv中,同时添加一条"全局异常...."的消息。如果是RuntimeException类型,则获取其消息并添加到mv中,同时添加一条"运行时异常...."的消息。
- 最后返回mv对象,Spring会根据这个对象渲染错误页面。
部分方法测试
4.3异常处理方式三
- ControllerAdvice:这是一个全局的异常处理器,可以捕获所有控制器中抛出的异常。通常我们会使用
@ControllerAdvice
注解来标记一个类为 ControllerAdvice,然后在该类中定义异常处理方法。 - @ExceptionHandler:这是一个用于处理方法参数中抛出的指定类型的异常的注解。我们可以在方法上使用
@ExceptionHandler
注解来指定要处理的异常类型,以及处理方法的名称。
package com.ctb.component; import com.ctb.exception.GlobalException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class GlobalExceptionResolver { // 跳转错误页面 // @ExceptionHandler // public ModelAndView handler(Exception e){ // ModelAndView mv = new ModelAndView(); // mv.setViewName("error"); // if (e instanceof GlobalException){ // GlobalException globalException = (GlobalException) e; // mv.addObject("ex",globalException.getMessage()); // mv.addObject("msg","全局异常...."); // }else if (e instanceof RuntimeException){ // RuntimeException runtimeException = (RuntimeException) e; // mv.addObject("ex",runtimeException.getMessage()); // mv.addObject("msg","运行时异常...."); // } // return mv; // } // 返回错误json数据 @ResponseBody @ExceptionHandler public Map handler(Exception e){ Map map = new HashMap(); if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; map.put("ex",globalException.getMessage()); map.put("msg","全局异常...."); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; map.put("ex",runtimeException.getMessage()); map.put("msg","运行时异常...."); }else { map.put("ex",e.getMessage()); map.put("msg","其它异常...."); } return map; } }
代码解析
handler(Exception e)
方法:
- 使用
@ResponseBody
注解,表示将返回的对象作为响应体发送给客户端。- 使用
@ExceptionHandler
注解,表示该方法用于处理控制器中抛出的异常。- 创建一个
HashMap
对象,用于存储异常信息。- 判断异常类型,如果是
GlobalException
,则将异常信息添加到map
中,并设置提示信息为"全局异常....";- 如果异常类型是
RuntimeException
,则将异常信息添加到map
中,并设置提示信息为"运行时异常....";- 其他类型的异常,将异常信息添加到
map
中,并设置提示信息为"其它异常....";- 返回
map
对象。
部分方法测试
5.响应封装类
- 创建自定义异常类BusinessException
BusinessException自定义异常类将继承RuntimeException异常,该异常类用于处理在程序代码运行过程所产生的运行时业务异常信息。
- 创建响应枚举类JsonResponseStatus
JsonResponseStatus响应枚举类用于自定义错误码。
- 创建响应封装类JsonResponseBody
JsonResponseBody响应封装类用于以JSON的形式统一输出错误信息。
// 响应封装类 @ResponseBody @ExceptionHandler public R handler(Exception e){ if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; return R.ok(666,"全局异常....",globalException.getMessage()); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; return R.ok(666,"运行异常....",runtimeException.getMessage()); }else { return R.ok(666,"其它异常....",e.getMessage()); } }
根据传入的异常对象进行类型判断,并返回相应的响应结果。
部分方法测试