You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中替换处理Throwable派生类型的if-else代码块?

优化Spring GlobalErrorAttributes的异常处理逻辑

问题背景

在Spring开发中,现有GlobalErrorAttributes类通过大量if-else分支判断异常类型,来填充错误属性并返回响应。随着业务扩展,异常类型逐渐增多,代码会愈发臃肿,且每次新增异常都需要修改核心类,违反开闭原则。需要实现无需修改原有核心代码即可扩展异常处理能力,同时利用Spring的自动装配机制,新增异常处理逻辑时自动生效。

现有核心代码

原GlobalErrorAttributes实现如下:

@Component
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> map = new LinkedHashMap<>();
        Throwable error = super.getError(request);
        HttpStatus errorStatus;
        String messageDetail = null;

        if (error instanceof UserAlreadyExistsException) {
            Throwable cause = error.getCause();
            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }
            errorStatus = HttpStatus.NOT_ACCEPTABLE;
            log.error("error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }

        if (error instanceof UserNotFoundException ||
                error instanceof ResponseStatusException || error instanceof PasswordInHashNotFoundException) {
            Throwable cause = error.getCause();
            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }
            errorStatus = HttpStatus.NOT_FOUND;
            log.error("error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        } else if (error instanceof BadSqlGrammarException) {
            Throwable cause = error.getCause();
            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }
            errorStatus = HttpStatus.NOT_ACCEPTABLE;
            log.error("BadSqlGrammar error: error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        } else {
            Throwable cause = error.getCause();
            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }
            errorStatus = HttpStatus.INTERNAL_SERVER_ERROR;
            log.error("Not defined error: error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }
        map.put("status", errorStatus.value());
        map.put("message", error.getMessage());
        map.put("detailMessage", messageDetail);
        return map;
    }
}

尝试过的方案及问题

  • 用Throwable实例作为Map键:无法通过instanceof匹配,因为Map中存储的是预先创建的异常实例,和实际抛出的异常不是同一个对象,导致匹配失败。
  • 用类名作为Map键:虽然能匹配具体异常类,但无法处理继承关系(子类异常无法匹配父类的处理逻辑),且硬编码类名容易出错,扩展性差。

可行解决方案

采用策略模式+Spring自动装配的方式,将每个异常的处理逻辑封装为独立策略类,由Spring自动扫描加载,核心类仅需遍历策略列表找到匹配项即可。

1. 定义异常处理策略接口

创建接口统一规范异常处理的核心行为:

public interface ExceptionHandlerStrategy {
    // 判断当前策略是否支持处理目标异常
    boolean supports(Throwable throwable);
    // 返回对应HTTP状态码
    HttpStatus getHttpStatus();
    // 返回日志打印模板
    String getLogTemplate();
    // 自定义错误属性填充(默认实现处理通用逻辑)
    default void customizeErrorAttributes(Map<String, Object> attributes, Throwable throwable) {
        String detailMessage = throwable.getCause() != null ? throwable.getCause().getMessage() : null;
        attributes.put("detailMessage", detailMessage);
    }
}

2. 实现具体异常处理策略

每个异常(或一组异常)对应一个策略类,标记@Component让Spring自动扫描装配:

用户已存在异常处理策略

@Component
public class UserAlreadyExistsExceptionHandler implements ExceptionHandlerStrategy {
    @Override
    public boolean supports(Throwable throwable) {
        return throwable instanceof UserAlreadyExistsException;
    }

    @Override
    public HttpStatus getHttpStatus() {
        return HttpStatus.NOT_ACCEPTABLE;
    }

    @Override
    public String getLogTemplate() {
        return "error - {}, status - {}, detailMessage - {}";
    }
}

未找到类异常处理策略

@Component
public class NotFoundExceptionHandler implements ExceptionHandlerStrategy {
    @Override
    public boolean supports(Throwable throwable) {
        return throwable instanceof UserNotFoundException 
                || throwable instanceof ResponseStatusException 
                || throwable instanceof PasswordInHashNotFoundException;
    }

    @Override
    public HttpStatus getHttpStatus() {
        return HttpStatus.NOT_FOUND;
    }

    @Override
    public String getLogTemplate() {
        return "error - {}, status - {}, detailMessage - {}";
    }
}

SQL语法错误异常处理策略

@Component
public class BadSqlGrammarExceptionHandler implements ExceptionHandlerStrategy {
    @Override
    public boolean supports(Throwable throwable) {
        return throwable instanceof BadSqlGrammarException;
    }

    @Override
    public HttpStatus getHttpStatus() {
        return HttpStatus.NOT_ACCEPTABLE;
    }

    @Override
    public String getLogTemplate() {
        return "BadSqlGrammar error: error - {}, status - {}, detailMessage - {}";
    }
}

默认异常处理策略

@Component
public class DefaultExceptionHandler implements ExceptionHandlerStrategy {
    @Override
    public boolean supports(Throwable throwable) {
        return true; // 匹配所有未被其他策略处理的异常
    }

    @Override
    public HttpStatus getHttpStatus() {
        return HttpStatus.INTERNAL_SERVER_ERROR;
    }

    @Override
    public String getLogTemplate() {
        return "Not defined error: error - {}, status - {}, detailMessage - {}";
    }
}

3. 改造GlobalErrorAttributes核心类

通过构造函数注入所有策略实现,遍历找到匹配的策略后执行处理逻辑:

@Component
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {

    private final List<ExceptionHandlerStrategy> handlerStrategies;

    // Spring自动注入所有ExceptionHandlerStrategy实现类
    public GlobalErrorAttributes(List<ExceptionHandlerStrategy> handlerStrategies) {
        this.handlerStrategies = handlerStrategies;
    }

    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> map = new LinkedHashMap<>();
        Throwable error = super.getError(request);
        
        // 查找匹配的处理策略
        ExceptionHandlerStrategy strategy = handlerStrategies.stream()
                .filter(handler -> handler.supports(error))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("No exception handler strategy found"));

        HttpStatus errorStatus = strategy.getHttpStatus();
        String detailMessage = error.getCause() != null ? error.getCause().getMessage() : null;

        // 打印日志
        log.error(strategy.getLogTemplate(),
                error.getMessage(),
                errorStatus,
                detailMessage);

        // 填充基础错误属性
        map.put("status", errorStatus.value());
        map.put("message", error.getMessage());
        // 执行策略自定义属性逻辑
        strategy.customizeErrorAttributes(map, error);

        return map;
    }
}

扩展新异常的方式

新增异常类型时,仅需创建一个ExceptionHandlerStrategy实现类并标记@Component,Spring会自动扫描并将其加入策略列表,无需修改原有核心代码。

方案优势

  • 符合开闭原则:新增异常处理无需修改核心代码,仅需添加策略类。
  • Spring自动装配:利用Spring组件扫描能力,自动加载所有策略实现。
  • 灵活性强:每个策略可自定义日志模板、状态码及错误属性填充逻辑。
  • 可维护性高:每个异常的处理逻辑隔离在独立类中,代码结构清晰。

内容的提问来源于stack exchange,提问作者skyho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:22:17