做了这么多年程序员,你真的还是只会用@ControllerAdvice来做全局的异常处理吗?

1、@ControllerAdvice注解说明

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] assignableTypes() default {};

    Class<? extends Annotation>[] annotations() default {};
}
1.1、内部注解说明

这段代码定义了一个名为 ControllerAdvice 的注解。让我们来逐个说明它的各个部分:

  • @Target({ElementType.TYPE}): 这表示该注解可以应用在类上。因此,ControllerAdvice 注解可以用于标记类。

  • @Retention(RetentionPolicy.RUNTIME): 该注解说明在运行时可以通过反射获取到这个注解的信息。

  • @Documented: 这表示当生成 Javadoc 文档时,注解会被包含在文档中。

  • @Component: 由于 @ControllerAdvice 是一个特殊类型的组件,因此使用了 Spring Framework 中的 @Component 注解,表明这是一个Spring管理的组件。

  • @AliasFor("basePackages"): 这个注解表示 value() 属性和 basePackages() 属性是互相别名,它们具有相同的作用。也就是说,你可以使用其中之一来指定要扫描的基础包。

  • String[] value() default {}: 通过 @AliasFor 表明 value 属性是用来指定需要扫描的基础包路径。

  • String[] basePackages() default {}: 通过 @AliasFor 表明 basePackages 属性也是用来指定需要扫描的基础包路径。

  • Class<?>[] basePackageClasses() default {}: 允许以类的方式指定需要扫描的基础包路径。

  • Class<?>[] assignableTypes() default {}: 允许以类的方式指定适用的控制器类型。

  • Class<? extends Annotation>[] annotations() default {}: 允许指定需要扫描的控制器注解类型。

总的来说,ControllerAdvice 注解提供了一种方便的方式来定义全局控制器建议。通过指定需要扫描的基础包路径、适用的控制器类型以及需要扫描的控制器注解类型,可以集中处理应用程序中抛出的异常,并进行全局的预处理。

1.2、联合其它注解使用

@ControllerAdvice 注解可以与以下注解联合使用:

  1. @ExceptionHandler: 用于定义全局的异常处理方法。

  2. @InitBinder: 用于自定义数据绑定规则,通常用于日期格式化等操作。

  3. @ModelAttribute: 用于添加全局范围的模型属性,以便在每个请求的视图中都可用。

这些注解的结合使用可以让我们更方便地管理全局性的异常处理、数据绑定规则和模型属性,从而提高代码的复用性和整体的灵活性。

2、实战demo

2.1、项目目录说明

2.2、测试代码[Controller]
package com.example.demo.controller;

import com.example.demo.response.MidGroundResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/22 17:21
 */
@RestController
@RequestMapping("/test1")
public class Test1Controller {


    @RequestMapping("/test")
    public MidGroundResult test() {
        int i = 3 / 0;
        return MidGroundResult.ok("");
    }
}
package com.example.demo.controller;

import com.example.demo.response.MidGroundResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/22 17:21
 */
@RestController
@RequestMapping("/test2")
public class Test2Controller {

    @RequestMapping("/test")
    public MidGroundResult test() {
        int i = 3 / 0;
        return MidGroundResult.ok("");
    }
}
package com.example.demo.controller.group1;

import com.example.demo.response.MidGroundResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/22 17:21
 */
@RestController
@RequestMapping("/test3")
public class Test3Controller {
    @RequestMapping("/test")
    public MidGroundResult test() {
        int i = 3 / 0;
        return MidGroundResult.ok("");
    }
}
package com.example.demo.controller.group1;

import com.example.demo.response.MidGroundResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/22 17:22
 */
@RestController
@RequestMapping("/test4")
public class Test4Controller {


    @RequestMapping("/test")
    public MidGroundResult test(Date date) {
        return MidGroundResult.ok(date);
    }
}
package com.example.demo.controller.group1;

import cn.hutool.json.JSONObject;
import com.example.demo.response.MidGroundResult;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/22 17:22
 */
@RestController
@RequestMapping("/test5")
public class Test5Controller {



    @RequestMapping("/test")
    public MidGroundResult test(Date date) {
        return MidGroundResult.ok(date);
    }

    /**
     *
     * @param modelMap 全局参数
     * @param jsonObject 具体的业务参数
     * @return
     */
    @PostMapping("/test1")
    public MidGroundResult test(ModelMap modelMap, @RequestBody JSONObject jsonObject) {
        Object globalAttr = modelMap.getAttribute("globalAttr");
        Object map = modelMap.getAttribute("map");
        jsonObject.set("globalAttr",globalAttr);
        jsonObject.set("map",map);
        return MidGroundResult.ok(jsonObject);
    }
}
2.2、异常处理
2.2.1、全局异常类
package com.example.demo.exception;

import com.example.demo.response.MidGroundResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/23 13:35
 */
@Slf4j
@ControllerAdvice
@Order(value = 0)
public class GobalException {

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public MidGroundResult<RuntimeException> runTimeExcepetion(RuntimeException ex){
        log.error("{}全局异常处理器","GobalException",ex);
        return MidGroundResult.error("全局异常处理器");
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public MidGroundResult excetion(RuntimeException ex){
        log.error("{}全局异常处理器","GobalException",ex);
        return MidGroundResult.error("全局异常处理器");
    }
}
2.2.2、指定包异常捕获类
package com.example.demo.exception;

import com.example.demo.response.MidGroundResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author fyw
 * @version 1.0
 * @description: 仅在某一个包生效【注意order对应的值可以介于全局和指定类之间】
 * basePackageClasses = {Test1Controller.class}代表这个类所在的包和子包下面的controller都会被捕获
 * value/basePackages = {"com.example.demo.controller.group1"}代表这个包和子包下面的controller都会被捕获
 * @date 2024/1/23 13:35
 */
@Slf4j
//@ControllerAdvice(basePackageClasses = {Test1Controller.class})
@ControllerAdvice(value = {"com.example.demo.controller.group1"})
@Order(value = 2)
public class SepcialPackagelException {

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public MidGroundResult<RuntimeException> runTimeExcepetion(RuntimeException ex){
        log.error("{},{}","SepcialPackagelException",ex);
        return MidGroundResult.error(ex);
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public MidGroundResult excetion(RuntimeException ex){
        log.error("{},{}","SepcialPackagelException",ex);
        return MidGroundResult.error("");
    }
}
2.2.3、指定注解异常捕获类
package com.example.demo.exception;

import com.example.demo.response.MidGroundResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author fyw
 * @version 1.0
 * @description: 仅针对某一类注解生效【注意order对应的值可以介于全局和指定类之间】
 * annotations = {RestController.class}将会扫描所有被 @RestController 注解标记的控制器类,然后应用在这些类上
 * @date 2024/1/23 13:35
 */
@Slf4j
@ControllerAdvice(annotations = {RestController.class})
@Order(value = 101)
public class SepcialAnnotationException {

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public MidGroundResult<RuntimeException> runTimeExcepetion(RuntimeException ex){
        log.error("{}注解过滤器","SepcialAnnotationException",ex);
        return MidGroundResult.error("注解过滤器");
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public MidGroundResult excetion(RuntimeException ex){
        log.error("{}注解过滤器","SepcialAnnotationException",ex);
        return MidGroundResult.error("注解过滤器");
    }
}
2.2.4、指定某个类的异常捕获类
package com.example.demo.exception;

import com.example.demo.controller.Test1Controller;
import com.example.demo.response.MidGroundResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author fyw
 * @version 1.0
 * @description: 仅在某一个类上生效【注意order对应的值可以设置为最小】
 * @date 2024/1/23 13:35
 */
@Slf4j
@ControllerAdvice(assignableTypes = {Test1Controller.class})
@Order(value = 1)
public class SepcialClasslException {

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public MidGroundResult<RuntimeException> runTimeExcepetion(RuntimeException ex){
        log.error("{}针对Test1Controller类淡出捕获的异常,不会走全局异常捕获类","SepcialClasslException",ex);
        return MidGroundResult.error("针对Test1Controller类淡出捕获的异常,不会走全局异常捕获类");
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public MidGroundResult excetion(RuntimeException ex){
        log.error("{}针对Test1Controller类淡出捕获的异常,不会走全局异常捕获类","SepcialClasslException",ex);
        return MidGroundResult.error("针对Test1Controller类淡出捕获的异常,不会走全局异常捕获类");
    }
}
2.3、测试场景
2.3.1、将GobalException异常处理器的order顺序优先级调整为最高0,进行异常捕获测试

<span style="background-color:#f8f8f8"><span style="color:#333333">测试结果是无论在controller目录还是group1目录下的controller类都是走的全局过滤器;
</span></span>
2.3.2、注解异常捕获类测试,将SepcialAnnotationException的order调为0,GobalException调为100

<span style="background-color:#f8f8f8"><span style="color:#333333">测试结果是无论在controller目录还是group1目录下的controller类都是走的注解过滤器,因为controller上面都有@RestController注解;
</span></span>
2.3.3、指定包异常捕获类测试,将SepcialAnnotationException的order调整为101,SepcialPackagelException的order调整为0

<span style="background-color:#f8f8f8"><span style="color:#333333">测试结果test3在group1包下面吗,走的是包过滤器;test2在controller包下面走的是全局过滤器
</span></span>
2.3.4、在2.3.3的基础上测试test1应该走的是SepcialClasslException,因为SepcialClasslException上指定了具体的Test1Controller类,并且order小于全局异常捕获器和注解异常捕获器

<span style="background-color:#f8f8f8"><span style="color:#333333">由以上测试场景,就可以在项目里面随心所欲的定义异常处理器了;
</span></span>
2.4、全局数据绑定
2.4.1、全局数据绑定类
package com.example.demo.bind;

import com.example.demo.controller.group1.Test5Controller;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author fyw
 * @version 1.0
 * @description: 仅作用于Test5Controller类
 * @date 2024/1/23 16:25
 */
@ControllerAdvice(assignableTypes = {Test5Controller.class})
public class InitBinderController {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        System.out.println("进入initBinder方法");
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }
}

测试结果如下:

2.5、全局模型属性
2.5.1、全局模型属性添加类
package com.example.demo.model;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

import java.util.HashMap;
import java.util.Map;

/**
 * @author fyw
 * @version 1.0
 * @description: TODO
 * @date 2024/1/23 16:37
 */
@ControllerAdvice
public class ModelVController {

    @ModelAttribute
    public void presetParam(Model model){
        model.addAttribute("globalAttr","this is a global attribute");
    }

    @ModelAttribute("map")
    public Map<String, String> presetParam(){
        Map<String, String> map = new HashMap<String, String>();
        map.put("key1", "value1");
        map.put("key2", "value2");
        map.put("key3", "value3");
        return map;
    }
}

测试结果:

2.6、公用类代码

package com.example.demo.response;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.example.demo.enums.MidGroundEnum;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * @author fyw
 * @version 1.0
 * @description: 
 * @date 2024/1/16 8:53
 */
@Data
public class MidGroundResult<T> {

    //返回码,00000成功
    private String errorCode;

    //返回值,说明信息
    private String errorMessage;

    //返回数据
    private T data;

    //业务响应时间 2023-05-25 18:18:18
    private String resultTime;

    //排查id
    private String traceId;

    public MidGroundResult(String errorCode, String errorMessage, T data, String resultTime, String traceId) {
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
        this.data = data;
        this.resultTime = resultTime;
        this.traceId = traceId;
    }


    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getResultTime() {
        return resultTime;
    }

    public void setResultTime(String resultTime) {
        this.resultTime = resultTime;
    }

    public String getTraceId() {
        return traceId;
    }

    public void setTraceId(String traceId) {
        this.traceId = traceId;
    }

    //正常响应
    public static <T> MidGroundResult ok(T data) {
        return new MidGroundResult(MidGroundEnum.SUCCESS.getCode(), MidGroundEnum.SUCCESS.getMsg(), data,
                DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN),
                IdUtil.getSnowflake(1, 1).nextIdStr());
    }

    //系统异常
    public static <T> MidGroundResult error(T data) {
        return new MidGroundResult(MidGroundEnum.ERROR.getCode(), MidGroundEnum.ERROR.getMsg(), data,
                DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN),
                IdUtil.getSnowflake(1, 1).nextIdStr());
    }

    //主动性返回异常信息-校验不通过等
    public static <T> MidGroundResult fail(T data, String code, String msg) {
        return new MidGroundResult(code, msg, data,
                DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN),
                IdUtil.getSnowflake(1, 1).nextIdStr());
    }

}
package com.example.demo.enums;

/**
 * @Author fyw
 * @Description 
 * @Date 8:59 2024/1/16
 * @Param
 * @return
 **/
public enum MidGroundEnum {

    SUCCESS("00000", "成功"),
    INTERNAL_CALL_ERROR("C1001", "内部服务调用错误"),

    ERROR("B1001", "系统错误");

    private final String code;

    private final String msg;

    MidGroundEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

添加公众号了解更多,定期分享、绝对实用,绝对对你有帮助!

### 回答1: 不是的,我并不能取代程序员。作为一种人工智能技术,我可以协助程序员完成一些重复性或模板化的任务,但是程序员仍然是非常重要的,因为他们拥有创造性和判断力,并且可以使用自己的技能解决问题和设计新系统。程序员和人工智能的关系应该是协作的,而不是竞争的。 ### 回答2: 目前来说,程序员不会被我取代。虽然人工智能和机器学习的发展迅速,但目前的技术水平还无法完全取代程序员的工作。编程需要深入的逻辑思维能力、抽象思维能力和创造力,这些是机器目前所无法达到的。程序员也不仅仅是写代码的工人,他们还需要根据需求进行系统设计、进行项目管理和团队协作等。这些职责是人类独有的,机器无法到。 此外,编程领域的技术和需求也在不断发展和变化,程序员需要进行学习和更新自己的技能和知识。他们需要学习新的编程语言、工具和框架来保持自己的竞争力。而机器并不具备学习的能力,无法自主地更新和适应新的技术。 尽管人工智能和机器学习的发展给某些重复性和机械化的编程工作带来了威胁,但人类的智慧和创造力仍然是不可替代的。程序员可以通过利用人工智能和机器学习等技术的优势,提高自己的工作效率和质量,从而更好地适应和应对未来的变化。 所以,总体上来说,在可预见的未来,程序员不会被完全取代。他们的角色和职责可能会发生变化,但他们的专业知识、创造力和人类独有的智慧仍将在编程领域中起到不可替代的作用。 ### 回答3: 作为一个人工智能助手,我认为虽然人工智能技术的发展迅猛,但程序员不会被完全取代。 首先,尽管人工智能技术能够模拟人类的智能和推理能力,但目前的人工智能仍然存在很多局限性。人工智能更擅长处理大量的重复性任务和规则化的工作,例如数据分析、图像识别等,然而对于复杂的创造性和判断性工作,人类的智慧和思维仍然无可替代。 其次,程序员不仅仅是编写代码的人,他们还在开发软件和系统的过程中扮演着重要的角色。程序员不仅需要具备编程能力,还需要了解问题的本质以及解决方案的设计。他们需要思考如何将复杂的需求转化为可执行的代码,解决问题并优化性能。这种创造性思维和问题解决能力是人工智能无法取代的。 此外,随着科技的发展,新兴技术的涌现会为程序员创造更多的就业机会。例如,物联网、大数据、云计算、区块链等领域的快速发展,需要程序员不断学习和适应新技术,以满足市场需求。 总之,虽然人工智能的发展给某些程序员工作带来了一定的压力,但是程序员的职能和价值仍然非常重要。他们具备独特的技能和专业知识,可以为人工智能提供支持和完善。未来,程序员需要不断提升自己的技术能力和专业素养,与人工智能实现良好的互补,共同推动科技的发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值