一、JSON数据返回
1.1.前言
JSON是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。JSON的常用场景包括:
- 前后端分离的项目中,后端向前端传送数据时 。
- Ajax异步访问数据。
- RPC远程调用。
除了JSON,还有其他的数据传输格式,如XML等。但是由于XML格式的特点,它在Web开发中使用较少。
1.2.Jackson的介绍
1.2.1.什么是Jackson
Jackson是一个Java库,用于将Java对象转换为JSON格式,以及将JSON格式转换为Java对象。它提供了一种简单的方式来序列化和反序列化Java对象,使得它们可以很容易地在Java应用程序和Web服务之间进行传输。
Jackson库是一个开源项目,由FasterXML开发。它是目前最流行的Java JSON库之一,被广泛应用于各种Java项目中。
优点:
- 容易使用,提供了高层次外观,简化常用的用例。
- 无需创建映射,API提供了默认的映射大部分对象序列化。
- 性能高,快速,低内存占用
- 创建干净的json
- 不依赖其他库
- 代码开源
1.2.2.常用注解
@JsonIgnore 用于忽略某个属性或方法,不参与序列化或反序列化。
@JsonProperty 用来指定序列化和反序列化的属性名映射。
@JsonSerialize 用来指定序列化时使用的类。
@JsonDeserialize 用来指定反序列化时使用的类。
@JsonInclude 用来指定包含的属性名。
@JsonIncludeAll 包含所有属性,除了上面提到的属性。
@JsonAnyGetter 用于处理Map中的值。
@JsonAnySetter 用于处理Map中的值。
@JsonUnwrapped 将JSON字符串中的包装类型(如List、Map等)转换为对应的Java对象。
@JsonFormat 用于格式化日期、时间和数字等类型的序列化/反序列化。
@JsonIgnoreProperties 作用在类上,用来说明有些属性在序列化/反序列化时需要忽略掉
下面是一个简单的示例:
假设有一个Person类:
public class Person { private String name; private int age; @JsonIgnoreProperties({"address"}) // 忽略address属性的序列化和反序列化 private Address address; // getter and setter methods... }
在序列化时,只有name和age属性会被序列化到JSON中:
ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(person);
在反序列化时,只有name和age属性会被反序列化为Java对象:
Person person = objectMapper.readValue(json, Person.class);
1.3.使用注解
1.3.1.导入依赖
<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>
1.3.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>
1.3.3.案例实战
@ResponseBody使用
@ResponseBody注解的作用是将Controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON
数据或者是XML数据。
在使用此注解之后不会再走视图解析器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
以下我会以JSON格式的不同情况来演示数据回显。
JsonController.java
@Controller @RequestMapping("/stu/json") public class JsonController { @Autowired private StudentBiz stubiz; /** * 返回List<T> * @param req * @param Student * @return */ @ResponseBody @RequestMapping("/list") public List<Student> list(HttpServletRequest req, Student Student){ PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Student> lst = this.stubiz.selectBySnamePager(Student, pageBean); return lst; } /** * 返回T * @param req * @param Student * @return */ @ResponseBody @RequestMapping("/load") public Student load(HttpServletRequest req, Student Student){ if(Student.getSid() != null){ List<Student> lst = this.stubiz.selectBySnamePager(Student, null); return lst.get(0); } return null; } /** * 返回List<Map> * @param req * @param Student * @return */ @ResponseBody @RequestMapping("/mapList") public List<Map> mapList(HttpServletRequest req, Student Student){ PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Map> lst = this.stubiz.mapListPager(Student, pageBean); return lst; } /** * 返回Map * @param req * @param Student * @return */ @ResponseBody @RequestMapping("/mapLoad") public Map mapLoad(HttpServletRequest req, Student Student){ if(Student.getSid() != null){ List<Map> lst = this.stubiz.mapListPager(Student, null); return lst.get(0); } return null; } @ResponseBody @RequestMapping("/all") public Map all(HttpServletRequest req, Student Student){ PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Student> lst = this.stubiz.selectBySnamePager(Student, pageBean); Map map = new HashMap(); map.put("lst",lst); map.put("pageBean",pageBean); return map; } @ResponseBody @RequestMapping("/jsonStr") public String jsonStr(HttpServletRequest req, Student Student){ return "clzEdit"; } }
通过以上的案例我们可以看到返回T和List<T>都可以通过Map来做到,所以我们在做需求的时候应当灵活应用,如果返回的是字符串虽然我们有这个jsp页面,但也不会走视图解析器,这一点我们前面也说了这里也验证了。给大家提一个小技巧,如果你的Controller类里面,都是返回的JSON数据可以将@ResponseBody注解在类上,如果我们的类上同时出现以下两个注解:@Controller和@ResponseBody就可以使用@RestController。
小贴士:
@Controller注解用于标识一个类是Spring MVC中的控制器,即处理用户请求并返回响应的组件。
@ResponseBody注解用于将方法返回的对象转换为JSON格式的字符串,并将其作为HTTP响应体发送给客户端。
因此,@RestController注解合集的含义是:将一个类标记为Spring MVC控制器,并使用@ResponseBody注解将方法返回的对象转换为JSON格式的字符串,以便于在浏览器或其他客户端中进行访问。
二、异常处理
2.1.为什么要全局异常处理
在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护 ,全局异常处理是指在应用程序中对所有异常进行捕获和处理,而不是仅仅对特定的异常进行处理。
以下是一些原因说明为什么要全局异常处理:
- 统一处理异常:全局异常处理可以确保在应用程序的所有部分都使用相同的异常处理逻辑,从而使代码更加一致和易于维护。
- 简化代码:通过将异常处理逻辑集中在一个地方,可以减少代码冗余,并使代码更易于阅读和维护。
- 提高安全性:全局异常处理可以帮助防止未处理的异常导致的潜在安全问题,例如泄露敏感信息或允许攻击者执行恶意代码。
- 更好的用户体验:通过全局异常处理,应用程序可以在出现异常时给出更有用和友好的错误消息,从而提高用户体验。
综上所述,全局异常处理可以使应用程序更加健壮、一致、易于维护和安全,同时提供更好的用户体验。
面试题:运行时异常和编译时异常的区别?
- 编译时异常(Checked Exception): 编译时异常是在编译阶段被检查出来的异常,必须进行处理,否则编译器会报错。常见的编译时异常有IOException、SQLException等。处理方式可以使用try-catch语句块来捕获和处理这些异常。
- 运行时异常(Runtime Exception): 运行时异常是在程序运行期间抛出的异常,如果不进行处理,程序会崩溃。常见的运行时异常有NullPointerException、ArrayIndexOutOfBoundsException等。这些异常通常是由程序逻辑错误引起的,因此无法在编译时进行检测。
2.2.异常处理思路
系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
具体来说,异常处理的思路包括以下几个方面:
- 确定异常类型:在进行异常处理之前,需要先确定可能会出现哪些异常情况,以及这些异常情况对应的异常类型。
- 添加异常处理代码:在程序中添加相应的异常处理代码,用于捕获可能出现的异常,并进行相应的处理。
- 处理异常:根据不同的异常类型,采取不同的处理方式。例如,对于运行时异常,可以采取打印错误信息等方式进行处理;对于受检异常,则需要在方法声明中添加throws关键字,并在调用该方法时进行try-catch处理。优化异常处理:在实际应用中,需要根据具体情况对异常处理进行优化。例如,可以使用多线程机制来提高程序的性能;或者使用日志系统来记录程序运行过程中出现的异常情况等。
2.3.SpringMVC异常分类
SpringMVC中的异常处理方式有三种:
- 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;
- 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器;
- 使用@ControllerAdvice + @ExceptionHandler
2.4.案例实战
2.4.1.异常处理方式①
SpringMVC中自带了一个异常处理器叫SimpleMappingExceptionResolver,该处理器实现了HandlerExceptionResolver 接口,全局异常处理器都需要实现该接口。
spring-mvc.xml
<!-- 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>
error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>系统繁忙</title> </head> <body> ${ex}<br> <img src="${pageContext.request.contextPath }/static/1.jpg" style="height: 1000px;width: 1550px;"> </body> </html>
页面跳转由SpringMVC来接管了,所以此处的定义默认的异常处理页面都应该配置成逻辑视图名。
我们没有配置这段代码之前,以下的页面是我们不想看到的,看看配置后是怎么样的吧?
配置异常处理:
2.4.2.异常处理方式②
创建一个名为exception的包将我们的GlobalException类放入其中。
GlobalException.java
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); } }
创建一个名为Component的包将我们的GlobalExceptionHandler类放入其中。
GlobalExceptionHandler.java
@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"); 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","运行时异常...."); }else{ mv.addObject("ex",e.getMessage()); mv.addObject("msg","其他异常...."); } return mv; } }
1.通过instanceof判断异常类型
2.通过设置mv.setView(new MappingJackson2JsonView())方式返回JSON数据
这时候我们来访问以下http://localhost:8080/liwenzyssm/stu/json/jsonStr
因为异常处理会根据我们的异常问题进行判断map保存输出到前端,所以在JSP页面上用EL表达式捕捉msg信息就可以知道问题是什么更为直观。
2.4.3.异常处理方式③
GlobalExceptionResolver.java
@ControllerAdvice public class GlobalExceptionResolver { // 返回错误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; } }
这种方式是将我们的错误信息进行map保存然后转换为JSON格式输出在页面上。
这时候我们来访问以下 http://localhost:8080/wenhaozyssm/stu/json/jsonStr
2.5.响应封装类
通过我刚刚的解释想必大家对异常处理有了一定的理解,但是大家有没有发现,异常处理类中反复的需要定义Map,随后.put添加数据,我们能否对以上代码进行优化呢?能!!下面请欣赏小编所需的R工具类。
public class R extends HashMap { public R data(String key, Object value) { this.put(key, value); return this; } public static R ok(int code, String msg) { R r = new R(); r.data("success", true).data("code", code).data("msg", msg); return r; } public static R error(int code, String msg) { R r = new R(); r.data("success", false).data("code", code).data("msg", msg); return r; } public static R ok(int code, String msg,Object data) { R r = new R(); r.data("success", true).data("code", code).data("msg", msg).data("data", data); return r; } public static R ok(int code, String msg, long count, Object data) { R r = new R(); r.data("success", true).data("code", code).data("msg", msg).data("count", count).data("data", data); return r; } }
GlobalExceptionResolver.java
@ControllerAdvice public class GlobalExceptionResolver { // 响应封装类 @ResponseBody @ExceptionHandler public Map handler(Exception e){ if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; return R.ok(500,"全局异常....",globalException.getMessage()); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; return R.ok(500,"运行时异常....",runtimeException.getMessage()); }else { return R.ok(500,"其他异常....",e.getMessage()); } } }
这时候我们来访问以下 http://localhost:8080/wenhaozyssm/stu/jsonsonStr