普通全局异常存在的问题 GlobalExceptionHandler

发布时间 2023-11-23 19:19:26作者: YangDanMua

简单搭建 Feign 框架

image

parent

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.17</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.5</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

server

@SpringBootApplication
public class AppServer {
    public static void main(String[] args) {
        SpringApplication.run(AppServer.class);
    }
}
@RestController
public class FileController {

    @GetMapping("download")
    public ResponseEntity<byte[]> download() {
        byte[] bytes = FileUtil.readBytes(new File("C:\\Users\\xxx\\Pictures\\企业微信截图_x-x-x-x.png"));
        return ResponseEntity.ok(bytes);
    }

}

client

@SpringBootApplication
@EnableFeignClients
public class AppClient {
    public static void main(String[] args) {
        SpringApplication.run(AppClient.class);
    }
}
public class R<T> {

    private String code;

    private String message;

    private T data;
}
@RestControllerAdvice
@Component
public class GlobalExceptionResolver {

    @ExceptionHandler(Exception.class)
    public R<?> exceptionResolver(Exception e) {
        R<?> r = new R<>();
        r.setMessage("系统错误");
        return r;
    }
}
@FeignClient(url = "http://127.0.0.1:9999/", name = "test")
public interface FileFeinClient {

    @GetMapping("download")
    ResponseEntity<byte[]> download();

}
@RestController
public class ClientFileController {

    @Autowired
    private FileFeinClient fileFeinClient;

    @GetMapping("filedownload")
    public ResponseEntity<byte[]> download(){
        ResponseEntity<byte[]> download = fileFeinClient.download();
        try {
            System.out.println("Sleep Sleep Sleep Sleep ");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("Sleep Complete");
        } catch (InterruptedException e) {

        }

        return download;
    }

}

操作

postman 调用 client,sleep 期间 cancel。

问题

然后 client 的 Controller 返回值会被 HttpEntityMethodProcessor 处理,在 HttpEntityMethodProcessor#handleReturnValue 写响应体(server 返回的,默认是 content-type: application/octet-stream,还未实际写入)。
咋这个方法的最后 writeWithMessageConverters 时,发现连接断开,于是报错,转而走全局异常处理,全局异常处理的 @RestControllerAdvice 是一个组合注解,含 @ResponseBody,于是对于返回值的处理走 RequestResponseBodyMethodProcessor,它处理返回值时写数据走(AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)),发现 ServletServerHttpResponse 已经写了 Content-Type,于是找 MessageConverter(这里就是 R -> application/octet-stream),于是没找到,最终抛出异常 HttpMessageNotWritableException。

isCommit
最终向 Response 写数据时,一旦写过(写缓存,即便没有通过网络发送),会设置标志位 committed 为 true,因此可以使用 HttpServletResponse.isCommitted 判断(除了这种情况,什么时候会 isCommited 且抛出异常)。