Spring Boot全局异常处理、全局数据、参数预处理

前期回顾:
创建Spring Boot项目
Spring Boot项目基础配置
Spring Boot上传文件

整个实现围绕着@ControllerAdvice注解来进行的,@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute、@InitBinder使用。

  • 全局异常处理:一般我们在实际项目中都会对异常进行一个全局处理,方便统一返回,利于前后端的交互,通过搭配@ExceptionHandler来实现全局异常处理。假设目前我们需要定义一个所有交易参数的全局异常,下面上代码
import java.util.Map;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class CustomExceptionHandler {

    //成功
    private final static int success = 0;

    //校验
    private final static int invalid = 100;
    
    @ExceptionHandler(Exception.class)
    public Map<String, Object> allException(Exception e) {
        log.error("捕获Exception异常", e);
        Map<String, Object> map = Maps.newHashMap();
        map.put("code", invalid);
        map.put("msg", e.getMessage());
        return map;
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public Map<String, Object> checkHandler(IllegalArgumentException e) {
        log.error("出现校验错误", e);
        Map<String, Object> map = Maps.newHashMap();
        map.put("code", invalid);
        map.put("msg", e.getMessage());
        return map;
    }
}

代码解释:首先我们自定义一个CustomExceptionHandler类,在此处大家可能会看到用的注解是@RestControllerAdvice,不了解的同学们可能会有点疑惑,其实@RestControllerAdvice与@ControllerAdvice注解就和@RestController和@Controller的关系一样,为了不在方法上重复定义@ResponseBody注解,所以就直接采用了@RestControllerAdvice。演示代码中可以看到定义了两个异常捕获处理,通过@ExceptionHandler()注解标明方法所针对的异常。其中,checkHandler方法所捕获的为IllegalArgumentException异常,当程序抛出IllegalArgumentException异常时,只会进入此方法进行处理,当发生其它异常时,则会进入allException方法进行处理。至于返回形式根据各位实际项目中需求可自行定义。

  • 全局数据添加:搭配@ModelAttribute注解来实现全局数据的定义
    准备两个Bean:
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Employee {

    private String name;

    private Department department;
}

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Department {

    private String name;

    private Long id;
}

定义全局数据:

import com.lei.tang.demo.domain.emp.Department;
import com.lei.tang.demo.domain.emp.Employee;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalConfig {

    @ModelAttribute(value = "employeeZhang")
    public Employee build() {
        Employee employee = new Employee();
        employee.setName("张三");
        Department department = new Department();
        department.setId(1L);
        department.setName("业务部");
        employee.setDepartment(department);
        return employee;
    }
}

测试:

import java.util.Map;

import com.lei.tang.demo.domain.emp.Department;
import com.lei.tang.demo.domain.emp.Employee;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/global")
public class GlobalController {

    @GetMapping("/modelAttribute")
    public Employee modelAttribute(Model model) {
        Map<String, Object> map = model.asMap();
        return (Employee) map.get("employeeZhang");
    }
}

代码解释:通过@RestControllerAdvice与@ModelAttribute注解搭配使用定义了一个全局数据,@ModelAttribute注解中的value值为后期我们取数据时的key,返回值为需要的值,在测试代码中我们可以看到通过Model类我们我们可以直接获取到定义的全局数据。

  • 参数预处理:从上面定义的两个Bean我们可以看到其中的name属性键名一致,假设场景,现在有一个form表单需提交员工属性和部门具体参数,那么就会发现一个问题,两个Bean中都存在name属性,应该怎么区分呢?这时我们就可以搭配@InitBinder注解来进行参数到达API接口前的一个赋值处理。
    定义处理器:
import com.lei.tang.demo.domain.emp.Department;
import com.lei.tang.demo.domain.emp.Employee;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalConfig {

    @InitBinder(value = "emp")
    public void initEmployee(WebDataBinder webDataBinder) {
        webDataBinder.setFieldDefaultPrefix("emp.");
    }

    @InitBinder(value = "department")
    public void initDepartment(WebDataBinder webDataBinder) {
        webDataBinder.setFieldDefaultPrefix("department.");
    }
}

测试:通过访问http://localhost:8088/global/initBinder?emp.name=张三&department.name=业务部&department.id=1

import java.util.Map;

import com.lei.tang.demo.domain.emp.Department;
import com.lei.tang.demo.domain.emp.Employee;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/global")
public class GlobalController {

    @GetMapping("/initBinder")
    public Employee initBinder(@ModelAttribute("emp") Employee employee, @ModelAttribute("department") Department department) {
        employee.setDepartment(department);
        return employee;
    }
}

代码解释:定义了两个处理器,两个处理器中的webDataBinder.setFieldDefaultPrefix方法是设置请求参数映射到Bean的属性时会自动加上指定的前缀,所以可以看到测试代码中的请求路径中的所以参数都加上的对应的前缀,否则无法映射到Bean的属性上。而@InitBinder(value = “emp”)与@ModelAttribute(“emp”)和@InitBinder(value = “department”)与@ModelAttribute(“department”)形成一一对应的关系。

如有不到之处,欢迎各位留言指正,不慎感激。
更多文章:
点击跳转CSDN博客
点击跳转简书博客
公众号:代码小搬运
扫码关注代码小搬运公众号

你可能感兴趣的:(#,Spring,Boot)