Spring Boot中异常处理的最佳实践


每当我们想到在代码的任何级别上处理异常时,我们都会陷入编写代码中尝试try块的任何地方,然后经过几天尝试遍历我们的代码后,我们发现大多数代码中都充满了处理异常。这会降低代码的可读性,还会降低许多记录器消息的重复性,而这很容易避免。在这里,我们将尝试学习Spring Boot提供的强大功能,以避免这些重复,并在处理应用程序中的异常时提高代码的可读性。

概述 众所周知,异常处理是Spring Boot Rest API中最重要也是至关重要的事情,它有助于我们对代码执行条件和无条件检查,并以适当的方式处理任何类型的异常。除了它的优点之外,它还使代码复杂化,并使未知用户不容易阅读该代码。不建议在代码的任何位置使用try catch块,因为我们无法正确读取代码,而且还会增加类中不需要的行。因此,我们需要有一个公共的地方,我们可以在其中管理和处理各种异常,并根据异常类型发送API响应的相应错误代码。在本博客中,我们将尝试了解一种简单的方法,使我们的代码具有更好的格式,与SpringBoot中提供的异常处理相关。

描述 SpringBoot在包org.springframework.web.bind.annotation下提供了一个非常强大的注释,称为@ControllerAdvide。此批注使我们的生活很容易在应用程序的中心位置处理各种异常。我们不需要在每个方法或类中分别捕获任何异常,而是可以从方法中抛出异常,然后将其捕获在由@ControllerAdvide注释的中央异常处理程序类下。任何带有@ControllerAdvice注释的类都将成为一个控制器建议类,它将负责处理异常。在此类下,我们使用@ ExceptionHandler,@ ModelAttribute,@ InitBinder提供的注释。

用@ExceptionHandler注释的异常处理方法将捕获已声明的类引发的异常,并且只要遇到相关的类型异常,我们就可以执行各种操作。

@ControllerAdvice构造函数带有一些特殊的参数,使您仅扫描应用程序的相关部分,并仅处理构造函数中提到的各个类引发的那些异常。默认情况下,它将扫描和处理应用程序中的所有类。以下是一些我们可以用来仅限制特定类来处理异常的类型。

1)注释-带有提到的注释的控制器将由@ControllerAdvice注释的类提供帮助,并且有资格获得这些类的例外

例如。@ControllerAdvice(annotations = RestController.class)-在这里,由@ControllerAdvice注释的异常帮助器将捕获@RestController注释类抛出的所有异常。

2)basePackages-通过指定我们要扫描的程序包并处理相同的异常。

例如。@ControllerAdvice(basePackages = "org.example.controllers")-这只会扫描调用提到的程序包,并处理相同的异常。

3)AssignableTypes-此参数将确保扫描并处理提到的类中的异常

例如。 @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})

在使用@ControllerAdvice之前 在下面的代码片段中,我们看到有很多重复的行,并且由于每个API中都有多个try和catch块,因此控制器代码不容易阅读。

@RestController
@RequestMapping(path = "/employees")
public class EmployeeController {

    private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);


    private EmployeeDao employeeDao;

    @GetMapping(path="/{employeeId}", produces = "application/json")
    public ResponseEntity<Employee> getEmployees(@PathVariable Long employeeId) {
        ResponseEntity<Employee> response = null;
        try {
        if(null==employeeId || positionId.equals(0L)) {
                throw new InvalidInputException("Employee Id is not valid");
            }
            employee = employeeDao.getEmployeeDetails(employeeId);
            response = new ResponseEntity<Employee>(employee,HttpStatus.OK);
        }
        catch(InvalidInputException e) {
            Logger.error("Invalid Input:",e.getMessage());
            response = new ResponseEntity<Employee>(employee,HttpStatus.BAD_REQUEST);
        }
        catch(BusinessException e) {
            Logger.error("Business Exception:",e.getMessage());
            response = new ResponseEntity<Employee>(employee,HttpStatus.INTERNAL_SERVER_ERROR);
        }
        catch(Exception e) {
           Logger.error("System Error:",e.getMessage());

            response = new ResponseEntity<Employee>(employee,HttpStatus.INTERNAL_SERVER_ERROR);

        }
        return response;
    }

    @GetMapping(path="/address/{employeeId}", produces = "application/json")
    public ResponseEntity<Address> getEmployeeAddress(@PathVariable Long employeeId,@RequestHeader Long userId) {
        ResponseEntity<Address> response = null;
        try {
            if(null==employeeId || positionId.equals(0L)) {
                throw new InvalidInputException("Employee Id is not valid");
            }
            if(null==userId || userId.equals(0L)) {
                throw new UnauthorizedException("Unauthorized user");
            }
            address = employeeDao.getEmployeeAddress(employeeId);
            response = new ResponseEntity<Address>(address,HttpStatus.OK);
        }
        catch(UnauthorizedException e) {

            Logger.error("Unauthorized:",e.getMessage());
            response = new ResponseEntity<Address>(address,HttpStatus.BAD_REQUEST);
        }
        catch(InvalidInputException e) {
            Logger.error("Invalid Input:",e.getMessage());
            response = new ResponseEntity<Address>(address,HttpStatus.BAD_REQUEST);
        }
        catch(Exception e) {
            Logger.error("System Error:",e.getMessage());
            response = new ResponseEntity<Address>(address,HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return response;

    }
}

使用@ControllerAdvice之后

下面的代码片段使代码易于阅读,并减少了重复行。

@RestController
@RequestMapping(path = "/employees")
public class EmployeeController {
    private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);
    @GetMapping(path="/{employeeId}", produces = "application/json")
    public ResponseEntity<Employee> getEmployees(@PathVariable Long employeeId) {
        if(null==employeeId || positionId.equals(0L)) {
            throw new InvalidInputException("Employee Id is not valid");
        }
        Employee employee = employeeDao.getEmployeeDetails(employeeId);
        return new ResponseEntity<Employee>(employee,HttpStatus.OK);;
    }

    @GetMapping(path="/address/{employeeId}", produces = "application/json")
    public ResponseEntity<Address> getEmployeeAddress(@PathVariable Long employeeId,@RequestHeader Long userId) {
        if(null==employeeId || employeeId.equals(0L)) {
            throw new InvalidInputException("Employee Id is not valid");
        }
        if(null==userId || userId.equals(0L)) {
            throw new UnauthorizedException("Unauthorized user");
        }
        Address address = employeeDao.getEmployeeAddress(employeeId,userId);
        return new ResponseEntity<Address>(address,HttpStatus.OK);
    }
}

@ControllerAdvice
public class ExceptionHelper {
    private static final Logger logger = LoggerFactory.getLogger(ExceptionHelper.class);


    @ExceptionHandler(value = { InvalidInputException.class })
    public ResponseEntity<Object> handleInvalidInputException(InvalidInputException ex) {
        LOGGER.error("Invalid Input Exception: ",ex.getMessage());
        return new ResponseEntity<Object>(ex.getMessage(),HttpStatus.BAD_REQUEST);
   }


    @ExceptionHandler(value = { Unauthorized.class })
    public ResponseEntity<Object> handleUnauthorizedException(Unauthorized ex) {
        LOGGER.error("Unauthorized Exception: ",ex.getMessage());
        return new ResponseEntity<Object>(ex.getMessage(),HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(value = { BusinessException.class })
    public ResponseEntity<Object> handleBusinessException(BusinessException ex) {
        LOGGER.error("Business Exception: ",ex.getMessage());
        return new ResponseEntity<Object>(ex.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(value = { Exception.class })
    public ResponseEntity<Object> handleException(Exception ex) {
        LOGGER.error("Exception: ",ex.getMessage());
        return new ResponseEntity<Object>(ex.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

结论 通过上面的示例,我们发现使用@ControllerAdvice将解决我们维护代码质量的许多问题,并提高代码的可读性。这将帮助每个人在应用程序的公共位置调试所有错误。我们还可以使用这种方法轻松地更新记录器中的任何类型的错误,并保持错误消息的一致性。


原文链接:http://codingdict.com