异常处理相关心得

这段时间发现异常捕获是非常重要的一个机制,之前光埋头写代码了,如今想来,除了代码之外,解决问题的能力也是非常重要的,而如何解决问题,异常的捕获不可或缺

@RestControllerAdvice

@RestControllerAdvice 主要用于全局异常处理,它通常能够捕获由
@Controller、@RestController 标注的类中抛出的异常,并对其进行统一处理,意味着从线程池中抛出来的异常就不能被这个类捕获到了

给出一个简单的代码

@RestControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(Exception.class)
    public String exceptionHandler(Exception e) {
        return "统一异常处理:" + e.getMessage();
    }
}

那么这个类能处理的异常就是在 controller 层面的异常,比如:

    @GetMapping("/initiative")
    public String test() {
        Date now = new Date();
        if (true) {
            throw new RuntimeException("主动抛出异常");
        }
        return "test";
    }
    
    //运行时异常--空指针异常/数组越界异常 等等
    @GetMapping("/runtime")
    public String runtime() {
        String str = null;
        return str.substring(0);
    }

    //受检异常--文件找不到异常 等等
    @GetMapping("/checked")
    public String checked() throws Exception {
        FileInputStream fis = new FileInputStream(new File("111datanew.xlsx"));
        return "checked";
    }

要注意,如果你 try catch 住了,就比如:

    //try-catch捕获异常
    @GetMapping("/trycatch")
    public String trycatch() {
        try {
            String str = null;
            String substring = str.substring(0);
            System.out.println("substring = " + substring);
        } catch (Exception e) {
        }
        return "trycatch2 请求成功";
    }

那么你收到的请求结果是 “trycatch2 请求成功”,而不是异常信息

线程池中的异常

实际上,线程池中的异常是比较难处理的一种异常,我们请求接口,然后接口中把一部分任务交给线程池,这通常是为了加快接口相应速度。一种是调用这个接口为的是开启一个任务,那么此时接口只需要封装一个任务交给线程池,然后直接返回 success 即可,另外一种是调整代码执行顺序,利用线程池,将一部分资源先准备好,等到后续用的时候可以更快的获取到。说白了就是,一种需要拿到返回值之后继续执行代码,最后返回success,一种不需要返回值,直接 success。

首先有一个线程池

@Configuration
public class ThreadPoolConfig {
    @Bean  //创建一个线程池
    public ThreadPoolExecutor myThreadPool() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5,
                1L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

第一种,需要返回值的,那只能是 Callable

public class MyFuture implements Callable<String> {
    @Override
    public String call() throws Exception {
        if (true) {
            Thread.sleep(1000);
            int i = 1 / 0;
        }
        return "线程池中的任务执行完成";
    }
}

需要注意的是,这里的 MyFuture 代码块中,并没有去 try catch,从而可以将异常带到controller层,使用@RestControllerAdvice进行捕获

    @GetMapping("/threadpool")
    public String threadpool() throws ExecutionException, InterruptedException {
        MyFuture myFuture = new MyFuture();
        FutureTask<String> futureTask = new FutureTask<>(myFuture);
        threadPoolExecutor.submit(futureTask);
        String s = futureTask.get();
        return "threadpool 请求成功";
    }

那么如果try catch了会怎么样呢?那自然RestControllerAdvice 就捕获不到,就会 return “threadpool 请求成功”;

第二种,不需要返回值的,那就可以是 Runnable

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        //像这么写,是不会在控制台打印出异常信息的,当然,我们可以将代码使用try-catch包裹起来,然后在catch中打印异常信息
        ArrayList<String> strings = new ArrayList<>();
        strings.add("1");
        strings.add("2");
        System.out.println(strings.get(3));
    }
}

不需要返回值的,如何捕获线程池中的异常?

    @GetMapping("/threadpool3")
    public String threadpool3() throws ExecutionException, InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.setUncaughtExceptionHandler((t, e) -> {
            System.out.println("线程池中的异常被捕获到了--2" + e.getMessage());//控制台打印异常信息
        });
        thread.start();
        return "threadpool-myRunnable 请求成功";//这里会被执行
    }

最后是 过滤器 里面出现的异常

过滤器里面出现的异常,并没有传播到 Spring mvc 里面,所以说也不会被 @RestControllerAdvice 捕获到,该怎么办呢,还是老老实实使用 try catch 吧

但是不论是什么异常捕获机制,都有疏漏的地方,就比如当我们请求 /threadpool3 的时候,得到了 threadpool-myRunnable 请求成功 这段话,就真的达到我们想要的目的了么?线程池里面的任务执行的如何了?失败的话,只是被打印出来的日志,没有人去看也没用,所以说,最好的方式还是捕获到异常之后,发送一条消息到 钉钉群,或者是微信群 这种方式最为稳妥。

没有人会点击一个接口之后,立马去看日志,当我们发现线程池中的任务执行失败的时候,很可能是1个小时以后的事情了,那个时候再去翻找日志太麻烦,所以说及时通知到群里才是最可靠的

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值