首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaEE 分層異常處理機制:從 DAO 到 Servlet 的最佳實踐

     編輯:Java 相關知識

分層異常處理核心原則

  1. DAO 層:捕獲 SQL 異常 → 事務回滾 → 轉換為非受檢異常向上拋出
  2. Service 層:捕獲 DAO 異常 → 業務邏輯判斷 → 包裝為統一業務異常(RuntimeException 子類)
  3. Web 層:統一異常攔截 → 映射到友好錯誤頁面,記錄完整堆棧供運維排查

代碼實戰:四層協作

1. 自定義業務異常基類

public class AppRuntimeException extends RuntimeException {
    private final String errorCode;  // 業務錯誤碼,前端可國際化
    public AppRuntimeException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    public AppRuntimeException(String errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }
    public String getErrorCode() { return errorCode; }
}

2. DAO 層:事務回滾 + 異常轉換

@Repository
public class UserDaoImpl implements UserDao {
    @Autowired private DataSource dataSource;

    public void insert(User user) {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            conn.setAutoCommit(false);
            // ... 執行 SQL
            conn.commit();
        } catch (SQLException e) {
            // ① 事務回滾
            if (conn != null) try { conn.rollback(); } catch (SQLException ex) { log.error("Rollback failed", ex); }
            // ② 轉換拋出(保留原始堆棧)
            throw new AppRuntimeException("DB_ERROR", "數據庫操作失敗", e);
        } finally {
            // ③ 關閉資源(或使用 try-with-resources)
            if (conn != null) try { conn.close(); } catch (SQLException ex) { log.error("Close failed", ex); }
        }
    }
}

3. Service 層:業務異常封裝

@Service
@Transactional  // 聲明式事務,自動回滾 RuntimeException
public class UserServiceImpl implements UserService {
    @Autowired private UserDao userDao;

    public void register(User user) {
        try {
            // 業務校驗
            if (userDao.exists(user.getUsername())) {
                throw new AppRuntimeException("USER_EXISTS", "用戶名已存在");
            }
            userDao.insert(user);
            // 其他業務邏輯:發送歡迎郵件、初始化配置等
        } catch (AppRuntimeException e) {
            // 業務異常直接向上拋出(@Transactional 會回滾)
            throw e;
        } catch (Exception e) {
            // 未預期異常包裝為系統錯誤
            throw new AppRuntimeException("SYS_ERROR", "系統內部錯誤", e);
        }
    }
}

4. Web 層:統一異常處理(Spring MVC)

5. web.xml 兼容配置(非 Spring MVC 項目)

<error-page>
    <exception-type>com.example.AppRuntimeException</exception-type>
    <location>/error/business.jsp</location>
</error-page>
<error-page>
    <exception-type>java.lang.RuntimeException</exception-type>
    <location>/error/system.jsp</location>
</error-page>

關鍵避坑指南

陷阱後果正確做法
DAO 捕獲 SQLException 後不拋出/吞噬事務不回滾、上層感知不到失敗回滾後 throw new AppRuntimeException(..., e)
Service 層 catch Exception 後 log 並 return null事務不回滾(非 RuntimeException)、調用方收到 null 導致 NPE包裝為 RuntimeException 子類拋出
反射調用時目標異常被 InvocationTargetException 包裹web.xml 無法匹配原始異常類型ControllerAdvice 中解包 e.getCause() 判斷真實類型
錯誤頁面顯示堆棧信息信息洩露、用戶體驗差生產環境僅顯示友好提示,日誌記錄完整堆棧

現代 Spring Boot 最佳實踐

@RestControllerAdvice
public class ApiExceptionHandler {
    @ExceptionHandler(AppRuntimeException.class)
    public R<Void> handleBiz(AppRuntimeException e) {
        return R.fail(e.getErrorCode(), e.getMessage());
    }
    @ExceptionHandler(Exception.class)
    public R<Void> handleSys(Exception e) {
        log.error("系統異常", e);
        return R.fail("SYS_ERROR", "系統繁忙");
    }
}
// 統一響應封裝
public class R<T> {
    private int code; private String msg; private T data;
    public static <T> R<T> fail(String code, String msg) { ... }
}

總結

分層異常處理的核心是「各司其職、層層轉換、統一入口」:DAO 负責資源清理與事務回滾,Service 封裝業務語義,Web 層統一映射到用戶可感知的反饋。配合 @ControllerAdvice/@RestControllerAdvice 實現零侵入的全局異常治理,是構建健壯 JavaEE 應用的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved