【SpringMVC】JSON注解&全局异常处理机制

🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚

🌟在这里,我要推荐给大家我的专栏《Spring MVC》。🎯🎯

🚀无论你是编程小白,还是有一定基础的程序员,这个专栏都能满足你的需求。我会用最简单易懂的语言,带你走进Spring MVC的世界,让你从零开始,一步步成为JAVA大师。🚀🏆

🌈让我们一起在SpringMVC的世界里畅游吧!🌈🌈

👉点击这里,就可以查看我的主页啦!👇👇

Java方文山的个人主页

🎁如果感觉还不错的话请记得给我点赞哦!🎁🎁

💖期待你的加入,一起学习,一起进步!💖💖

目录

一、JSON数据返回

1.1.前言

1.2.Jackson的介绍

1.2.1.什么是Jackson

1.2.2.常用注解

1.3.使用注解

1.3.1.导入依赖

1.3.2.配置spring-mvc.xml

1.3.3.案例实战

二、异常处理

2.1.为什么要全局异常处理

2.2.异常处理思路

2.3.SpringMVC异常分类

2.4.案例实战

2.4.1.异常处理方式①

2.4.2.异常处理方式②

2.4.3.异常处理方式③

2.5.响应封装类


一、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类:

java 复制代码
public class Person {
    private String name;
    private int age;
    @JsonIgnoreProperties({"address"}) // 忽略address属性的序列化和反序列化
    private Address address;
    // getter and setter methods...
}

在序列化时,只有name和age属性会被序列化到JSON中:

java 复制代码
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(person);

在反序列化时,只有name和age属性会被反序列化为Java对象:

java 复制代码
Person person = objectMapper.readValue(json, Person.class);

1.3.使用注解

1.3.1.导入依赖

XML 复制代码
<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

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

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";
    }

}

返回List<T>:

返回T:

返回List<Map>:

返回Map:

返回JSON数组:

返回字符串:

总结:

通过以上的案例我们可以看到返回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中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护 ,全局异常处理是指在应用程序中对所有异常进行捕获和处理,而不是仅仅对特定的异常进行处理。

以下是一些原因说明为什么要全局异常处理:

  1. 统一处理异常:全局异常处理可以确保在应用程序的所有部分都使用相同的异常处理逻辑,从而使代码更加一致和易于维护。

  2. 简化代码:通过将异常处理逻辑集中在一个地方,可以减少代码冗余,并使代码更易于阅读和维护。

  3. 提高安全性:全局异常处理可以帮助防止未处理的异常导致的潜在安全问题,例如泄露敏感信息或允许攻击者执行恶意代码。

  4. 更好的用户体验:通过全局异常处理,应用程序可以在出现异常时给出更有用和友好的错误消息,从而提高用户体验。

综上所述,全局异常处理可以使应用程序更加健壮、一致、易于维护和安全,同时提供更好的用户体验。

小贴士:运行时异常和编译时异常的区别?

  1. 编译时异常(Checked Exception): 编译时异常是在编译阶段被检查出来的异常,必须进行处理,否则编译器会报错。常见的编译时异常有IOException、SQLException等。处理方式可以使用try-catch语句块来捕获和处理这些异常。

  2. 运行时异常(Runtime Exception): 运行时异常是在程序运行期间抛出的异常,如果不进行处理,程序会崩溃。常见的运行时异常有NullPointerException、ArrayIndexOutOfBoundsException等。这些异常通常是由程序逻辑错误引起的,因此无法在编译时进行检测。

2.2.异常处理思路

系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。

具体来说,异常处理的思路包括以下几个方面:

  1. 确定异常类型:在进行异常处理之前,需要先确定可能会出现哪些异常情况,以及这些异常情况对应的异常类型。

  2. 添加异常处理代码:在程序中添加相应的异常处理代码,用于捕获可能出现的异常,并进行相应的处理。

  3. 处理异常:根据不同的异常类型,采取不同的处理方式。例如,对于运行时异常,可以采取打印错误信息等方式进行处理;对于受检异常,则需要在方法声明中添加throws关键字,并在调用该方法时进行try-catch处理。

  4. 优化异常处理:在实际应用中,需要根据具体情况对异常处理进行优化。例如,可以使用多线程机制来提高程序的性能;或者使用日志系统来记录程序运行过程中出现的异常情况等。

2.3.SpringMVC异常分类

SpringMVC中的异常处理方式有三种:

  • 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;

  • 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器;

  • 使用@ControllerAdvice + @ExceptionHandler

2.4.案例实战

2.4.1.异常处理方式①

SpringMVC中自带了一个异常处理器叫SimpleMappingExceptionResolver,该处理器实现了HandlerExceptionResolver 接口,全局异常处理器都需要实现该接口。

spring-mvc.xml

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

XML 复制代码
<%@ 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

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

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/xwzyssm/stu/json/jsonStr

因为异常处理会根据我们的异常问题进行判断map保存输出到前端,所以在JSP页面上用EL表达式捕捉msg信息就可以知道问题是什么更为直观。

2.4.3.异常处理方式③

GlobalExceptionResolver.java

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/xwzyssm/stu/json/jsonStr

2.5.响应封装类

通过我刚刚的解释想必大家对异常处理有了一定的理解,但是大家有没有发现,异常处理类中反复的需要定义Map,随后**.put添加数据** ,我们能否对以上代码进行优化呢?能!!下面请欣赏小编所需的R工具类。

R.java

java 复制代码
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

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/xwzyssm/stu/json/jsonStr

到这里我的分享就结束了,欢迎到评论区探讨交流!!

💖如果觉得有用的话还请点个赞吧 💖

相关推荐
龙侠九重天3 分钟前
大型语言模型结构化输出:用 JSON Schema 约束大模型输出
人工智能·语言模型·自然语言处理·大模型·json
Volunteer Technology11 分钟前
SpringAi Chat Memory 聊天记忆(五)
人工智能·spring
斌果^O^13 分钟前
普通 SpringBoot 单体项目改造成微服务(Nacos+Gateway + 内部服务免鉴权)
java·spring boot·spring
tongluowan00711 小时前
Spring MVC 底层工作流程+源码分析
java·spring·mvc
Hexian258012 小时前
SpringAI系列(基础概念&springai系列 API)
spring·ai
Volunteer Technology15 小时前
SpringAI Chat Client (四)
人工智能·spring
ShiJiuD66688899916 小时前
springboot基础篇
java·spring boot·spring
敲敲千反田16 小时前
Spring AI
java·人工智能·spring
拽着尾巴的鱼儿17 小时前
spring 动态代理
java·后端·spring
云烟成雨TD17 小时前
Spring AI Alibaba 1.x 系列【52】Interrupts 中断机制:案例演示
java·人工智能·spring