Springboot多个异常处理类catch顺序

本文讲解如何在SpringBoot中确保特定异常处理类的优先级,通过@Order注解设置异常处理类的排序,使子类异常处理器优先于父类执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Springboot多个异常处理类catch顺序

通常情况下,我们都会定义一个全局异常处理类来处理异常,但是当我们定义了多个异常处理类,同时他们又存在父子继承关系的情况下,我们该怎么保证子类优先判断异常是否是自己可以捕获的呢?
如果父类先捕获该异常,那么该异常可能本该被子类处理掉,结果被父类的逻辑处理了,这肯定不是我们想要的。
所以springboot中的多个异常处理逻辑该怎么实现呢?

准备工作

1,引入springboot的springmvc整合包
2,创建两个异常类,这里面我们使用ConstraintViolationException参数检验异常类和Exception统一异常类两个。ConstraintViolationException是Exception的子类。

@ControllerAdvice
public class ArgumentsValidateAdvice {
   @ResponseBody
   @ExceptionHandler(value = ConstraintViolationException.class) //参数校验异常
   public String ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
      Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
      Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
      if (iterator.hasNext()) {
         ConstraintViolation<?> cvl = iterator.next();
         return ResponseUtil.appFail(cvl.getMessage());
      }
      return ResponseUtil.appFail("参数异常");
   }
}
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(Exception.class)  //表示catch什么异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public String exception(Exception e, HttpServletResponse response) {
        logger.error("global exception msg : {}", e.getMessage(), e);
        return ResponseUtil.appFail(response, "服务器异常!");
    }
}

3, 随便写一个SpringMvc的controller并提供一个外部请求的方法,我们只需要在方法的入参上面添加@NotNull @NotEmpty等参数检验注解即可
注:没使用过Spring mvc的参数校验的同学可以使用其他的异常或者自定义异常,原理都一样,只要是继承Exception的异常就好了,主要是保证多个异常间存在父子关系就好。

4,启动springboot,访问接口,然后我们估计不带参数,这时候参数校验就会抛出异常。
这时候我们是希望ArgumentsValidateAdvice 捕获到我们的参数异常,并返回给前端该异常信息的。如果是GlobalExceptionHandler先获取到了异常,那么他就会放回给前端“服务器异常!”,这显然不是我们想要的。
所以我们要保证ArgumentsValidateAdvice在异常捕获过程中要先于GlobalExceptionHandler。

一般都会想到实现Ordered接口或者点解@Order注解设置bean的排序优先级。那么这样真的可以吗,让我们debug看一下

在异常处理类的异常处理方法上打上断点

当SpringMvc发生异常的时候,会调用org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException.
在这里插入图片描述
这个方法会从exceptionHandlerAdviceCache中获取合适的异常处理类来处理发生的异常,所以我们下面应该继续找exceptionHandlerAdviceCache中的元素是怎么放进去的

我们定位到了org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache方法
在这里插入图片描述
可以看到是这里往exceptionHandlerAdviceCache中插入的元素。
所以我们需要在往exceptionHandlerAdviceCache插入元素的时候,让ArgumentsValidateAdvice 能在GlobalExceptionHandler之前放入。这样发生异常的时候ArgumentsValidateAdvice 就能被先拿出来考虑是不是符合捕获要求

上面的方法先通过org.springframework.web.method.ControllerAdviceBean#findAnnotatedBeans找到所有的ControllerAdvice注解的bean。
然后重点来了,就是AnnotationAwareOrderComparator.sort(adviceBeans);
点进去看到,这个排序是根据org.springframework.core.OrderComparator#compare来排
序的。
在这里插入图片描述
可以看到先根据是否继承PriorityOrdered来排,如果没有在调用getOrder()方法来排序
在这里插入图片描述
也就是根据是否是Ordered实例,如果是的话取Ordered的值来排序。
那么我们就需要让我们的advice是Order的实例。
方法调用到这里,我们继续跟踪得到上面图中的(Ordered) obj.getOrder()方法最终调用的是:
org.springframework.web.method.ControllerAdviceBean#getOrder

在这里插入图片描述
也就是说我们的advice被包装成了ControllerAdviceBean对象

所以我们需要看ControllerAdviceBean的order是哪里来的。
在这里插入图片描述
我们发现是在ControllerAdviceBean的构造方法中通过org.springframework.web.method.ControllerAdviceBean#initOrderFromBeanType对order进行的赋值。
在这里插入图片描述
继续看org.springframework.core.annotation.OrderUtils#getOrder(java.lang.Class<?>, java.lang.Integer)
在这里插入图片描述

注意,这个参数type就是我们的ArgumentsValidateAdvice等异常捕获advice
我们发现,如果我们的advice有Order注解,他就会取注解里面的值来设置order,如果没有该注解,他就会使用默认值,也就是Ordered.LOWEST_PRECEDENCE。

综上:

异常捕获类的排序只需要添加@Order注解,并指定order的值即可。
所以最后我们在ArgumentsValidateAdvice上添加@Order(Ordered.LOWEST_PRECEDENCE - 2),只要保证比GlobalExceptionHandler 的order靠前即可。

<think>好的,我需要帮用户解决如何使用Spring Boot通过FTP连接服务器并读取多个CSV文件的问题。首先,用户提到了引用的内容,特别是引用[1]中提到了SpringBoot和FTP的文件操作,引用[2]涉及Spring Batch处理CSV文件,引用[3]则是关于Spring Boot加载机制。不过用户的问题主要集中在FTP连接和读取多个CSV文件上,所以可能需要结合引用[1]和引用[2]的内容。 首先,我需要确定用户可能需要哪些步骤。通常,使用Spring Boot集成FTP需要添加相关依赖,比如Apache Commons Net或者Spring Integration FTP。引用[1]中提到了引包和配置,所以应该先确认依赖是否正确。然后配置FTP连接参数,如主机地址、端口、用户名、密码等,这部分可能在application.properties里配置。 接下来是编写FTP连接的代码。引用[1]提到了配置、接口服务和Controller层,所以可能需要创建一个FTP配置来初始化FTPClient,并编写服务来处理文件的上传、下载等。用户需要的是读取多个CSV文件,所以可能需要列出远程目录下的所有文件,过滤出CSV文件,然后逐个下载并处理。 然后,处理CSV文件可能会用到Spring Batch,如引用[2]中的示例,读取CSV并写入数据库。但用户可能只需要读取文件内容,不一定需要写入数据库,所以可能需要调整这部分代码。或者,用户可能希望将读取的CSV数据用于其他处理,比如业务逻辑或数据转换。 此外,需要考虑异常处理和资源释放,确保FTP连接在使用后正确关闭,避免资源泄漏。同时,多文件读取可能需要并发处理或顺序处理,根据用户的需求来决定。 用户的问题还提到要生成相关问题,可能涉及性能优化、错误处理、如何集成其他组件如Spring Batch,或者如何处理大文件等。 现在需要将这些步骤结构化,分点回答,确保代码示例正确,并且符合用户提供的引用内容。例如,引用[1]中的配置可以作为参考,而引用[2]中的Spring Batch示例可以用来展示如何处理CSV文件。同时,确保使用正确的依赖和配置方式,比如在pom.xml中添加commons-net和Spring Integration FTP的依赖,配置FTP参数,然后在服务中实现连接、登录、列出文件、下载文件等步骤。 最后,验证代码示例的正确性,确保FTPClient的使用方法正确,比如被动模式、文件型设置,以及正确关闭连接。同时,处理CSV文件可能需要使用OpenCSV或其他库来解析,这部分也需要在代码中体现。</think>以下是基于Spring Boot实现FTP连接并读取多份CSV文件的完整实现步骤: ### 一、添加依赖 在`pom.xml`中添加Apache Commons Net和Spring Integration FTP依赖: ```xml <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-ftp</artifactId> </dependency> ``` ### 二、配置FTP参数 在`application.yml`中配置: ```yaml ftp: host: 192.168.1.100 port: 21 username: admin password: pass123 remote-dir: /data/csv/ local-dir: ./downloads/ ``` ### 三、核心实现代码 #### 1. 配置封装FTP参数 ```java @Configuration @ConfigurationProperties(prefix = "ftp") @Data public class FtpConfig { private String host; private int port; private String username; private String password; private String remoteDir; private String localDir; } ``` #### 2. FTP文件下载服务 ```java @Service public class FtpService { @Autowired private FtpConfig config; public List<File> downloadCsvFiles() throws IOException { FTPClient ftp = new FTPClient(); List<File> downloadedFiles = new ArrayList<>(); try { ftp.connect(config.getHost(), config.getPort()); ftp.login(config.getUsername(), config.getPassword()); ftp.enterLocalPassiveMode(); ftp.setFileType(FTP.BINARY_FILE_TYPE); FTPFile[] files = ftp.listFiles(config.getRemoteDir(), f -> f.getName().endsWith(".csv")); for (FTPFile file : files) { Path localPath = Paths.get(config.getLocalDir(), file.getName()); try (OutputStream os = Files.newOutputStream(localPath)) { ftp.retrieveFile(file.getName(), os); downloadedFiles.add(localPath.toFile()); } } return downloadedFiles; } finally { if (ftp.isConnected()) { ftp.disconnect(); } } } } ``` #### 3. CSV文件处理(集成Spring Batch) ```java @Bean public FlatFileItemReader<MyData> csvReader(Resource resource) { return new FlatFileItemReaderBuilder<MyData>() .name("csvReader") .resource(resource) .delimited() .names("field1", "field2", "field3") .targetType(MyData.class) .build(); } ``` ### 四、完整调用示例 ```java @RestController public class FileController { @Autowired private FtpService ftpService; @GetMapping("/import-csv") public String importCsv() { try { List<File> files = ftpService.downloadCsvFiles(); files.forEach(file -> { // 调用CSV处理逻辑 processCsv(file); }); return "成功导入" + files.size() + "个文件"; } catch (IOException e) { return "文件下载失败:" + e.getMessage(); } } } ``` ### 关键点说明 1. **连接管理**:使用`FTPClient`时需确保`connect/login/disconnect`的完整生命周期管理 2. **被动模式**:`enterLocalPassiveMode()`可解决防火墙限制问题[^1] 3. **文件过滤**:通过`listFiles()`的FilenameFilter参数实现CSV文件筛选 4. **批处理集成**:结合Spring Batch可实现大数据量CSV的高效处理[^2]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值