Jersey和Spring中的全局异常处理?

10

我正在使用Jersey&Spring 3.2以及Open CMIS开发RESTful Web服务。

我没有使用Spring的MVC模式,只使用了Spring IOC和Jersey SpringServlet,控制器类类似于以下代码:

@GET
@Path("/{objId:.+}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)

public statusMsg addObject(@PathParam("objId") String objId{

    return repoService.addObject(objId);
}

在 repoService 中,我正在使用 CMIS 执行添加对象的业务逻辑,我的问题是,我捕获了与 CMIS 相关的大约 5 个异常,然后是基本异常即 Exception,但对于每个服务方法,我都必须重复它,这是我不想做的。

我在谷歌上搜索并发现 @ControllerAdvice 是这种问题的最佳解决方案,您可以在其中定义所有已检查和未检查的异常,并从应用程序中删除所有 try catch 块。但它仅适用于 MVC 模式。

问题1:我是否可以在上面的 Jersey-Spring 框架中使用它?

经过更多研究,我发现Jersey 提供了 ExceptionMapper 来处理自定义异常,但我想捕获更多的 CMIS 异常或默认异常或 IO 异常等。

问题2: 我如何使用 ExceptionMapper 实现它?

问题3:我走在正确的道路上吗?还是您建议任何更好的方法来处理这些问题。

提前致谢。

2个回答

9

我使用Jersey 2.11与Tomcat,并且几乎所有的异常都通过ExceptionMapper处理。(在领域逻辑中,只有DB回滚过程使用try-catch代码。)

我认为带有@Provider的ExceptionMapper可以自动选择正确的ExceptionMapper。因此,我认为这个功能能够满足“我想捕获更多的CMIS异常或默认异常或IO异常等”的需求。

以下是我的ExceptionMapper设计代码:

1.一些Jersey根资源类

@GET
@Produces("application/json")
public String getUser(@NotNull @QueryParam("id") String id, 
  @NotNull @QueryParam("token") String token) throws Exception { // This level throws exceptions handled by ExceptionMapper

  someComplexMethod(id, token); // possible throw Exception, IOException or other exceptions.

  return CLICHED_MESSAGE;
}

2.异常映射器包。com.yourdomain.exceptionmapper

AbstractExceptionMapper.java(所有的ExceptionMapper类都继承自这个抽象类)

public abstract class AbstractExceptionMapper {
  private static Logger logger = LogManager.getLogger(); // Example log4j2.

  protected Response errorResponse(int status, ResponseEntity responseEntity) {
    return customizeResponse(status, responseEntity);
  }

  protected Response errorResponse(int status, ResponseEntity responseEntity, Throwable t) {
    logger.catching(t); // logging stack trace.

    return customizeResponse(status, responseEntity);
  }

  private Response customizeResponse(int status, ResponseEntity responseEntity) {
     return Response.status(status).entity(responseEntity).build();
  }
 }

ExceptionMapper.java(至少此映射器可以捕获任何未定义指定异常映射器的异常。)

@Provider
 public class ExceptionMapper extends AbstractExceptionMapper implements
 javax.ws.rs.ext.ExceptionMapper<Exception> {

 @Override
 public Response toResponse(Exception e) {
 // ResponseEntity class's Member Integer code, String message, Object data. For response format.
 ResponseEntity re = new ResponseEntity(Code.ERROR_MISC); 

  return this.errorResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, re, e);
 }
}

WebApplicationExceptionMapper.java(指定WebApplicationException)

@Provider
public class WebApplicationExceptionMapper extends AbstractExceptionMapper implements
    ExceptionMapper<WebApplicationException> {

  @Override
  public Response toResponse(WebApplicationException e) {
    ResponseEntity re = new ResponseEntity(Code.ERROR_WEB_APPLICATION);

    return this.errorResponse(e.getResponse().getStatus(), re, e);
  }
}

ConstraintViolationExceptionMapper.java(指定Hibernate Validator ConstraintViolationException)

@Provider
public class ConstraintViolationExceptionMapper extends AbstractExceptionMapper implements
    ExceptionMapper<ConstraintViolationException> {

  @Override
  public Response toResponse(ConstraintViolationException e) {
    ResponseEntity re = new ResponseEntity(Code.ERROR_CONSTRAINT_VIOLATION);

    List<Map<String, ?>> data = new ArrayList<>();
    Map<String, String> errorMap;
    for (final ConstraintViolation<?> error : e.getConstraintViolations()) {
      errorMap = new HashMap<>();
      errorMap.put("attribute", error.getPropertyPath().toString());
      errorMap.put("message", error.getMessage());
      data.add(errorMap);
    }

    re.setData(data);

    return this.errorResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, re, e);
  }
}

..和其他指定的异常可以创建ExceptionMapper类。

根据我的经验,异常映射器是一个高级概念,专注于领域逻辑。它可以从领域逻辑中驱除无聊的散乱try-catch块代码。 因此,我希望在问题3中你能感受到“Yes i am”,以解决您的环境中的问题。

您没有在应用程序中使用try catch和throw。

我的代码设计在方法中使用throws,这使得通过ExceptionMapper类进行管理。

public String getUser(@NotNull @QueryParam("id") String id, 
  @NotNull @QueryParam("token") String token) throws Exception

在上述方法中,我创建了一个类来处理所有可能出现的异常,对于任何未知异常,都可以使用基本异常来捕获。 现在,在我的应用程序中,无论何时发生任何异常,它都会进入CentralControllerException,并返回适当的响应和HTTP状态码。 Q.2. 您是否预见到上述方法存在任何问题。
我认为,如果是简单的项目或从不更新/修改项目(项目生命周期短),您的一类异常映射器方法就可以了。 但是...我从未采用过这种方法。简单地说,如果需要管理更多的异常,则此方法变得复杂且难以阅读和维护。 在我的策略中,面向对象编程应该使用多态策略来计划代码(类计划、DI计划),并且这种方法的某些部分旨在消除代码中的if/switch块。这个想法使每个方法的代码变得简短和简单,清晰地表达“领域逻辑”,并使代码变得更加耐用。
所以我创建了实现ExceptionMapper并委托给DI的类,该ExceptionMapper类管理异常。(因此,DI管理替换您的单个类If块管理的异常处理,这通常是一种重构方法,类似于Extract xxx http://refactoring.com/catalog/extractClass.html。在我们的讨论案例中,单个类和一个方法太忙了,因此采用每个ExceptionMapper类的方法,并且DI调用适当的类和方法策略。)
顺便说一下,系统处理结果在目前阶段是相同的。但是,如果需要减少未来的开发成本,则不应采用一类异常处理计划的方法。因为如果放弃简单的代码和重构状态,项目代码会更快死亡。
这是我的想法和原因。
问候。

你的示例中的 'Code' 类(Code.ERROR_MISC)来自哪个库? - jon

0

谢谢您的回复。我可以看到您已基于异常类型和行为创建了多个类。

Q1. 在您的服务方法中,是否会抛出任何异常?

public void addObject(String objId) throws WebApplicationException{ 
}

或者你在整个应用程序中没有使用try catch和throw。

实际上,我尝试了一些东西,在我的Web应用程序中我没有使用任何try、catch和throws,在我的CentralControllerException中我已经如下所述:

public class CentralControllerHandler implements ExceptionMapper<Exception> {

@Override
@Produces(MediaType.APPLICATION_JSON)
public Response toResponse(Exception ex) {

    if(ex instanceof CmisContentAlreadyExistsException){

        log.error(ex);
        // send Response status as 400
    }

    if(ex instanceof IOException){

        log.error(ex);
        // send Response status as 400
    }

    return Response;

}

}

所以在上述方法中,我只创建了一个类来处理所有可能出现的异常,对于任何未知的异常,都会有基本的Exception来捕获。

现在,在我的应用程序中,无论在哪里发生异常,它都会传递到CentralControllerException,并发送适当的响应和HTTP状态码。

Q.2. 你是否预见到上述方法中可能存在的问题?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接