Exception Handler

Objetivo

Documentar e/ou simplificar o tratamento de exceções.

Propósito

O tratamento de exceções pode ser bastante complexo de diversificado conforme onde o tratamento é feito e o tipo de aplicação a construir.

Existem dois tipos de tratamento de exceção

  1. Encaspular uma exceção em outra

  2. Transformar uma exceção em informação a retornar

A primeira é utilizada em processo entre camadas - normalmente via AOP - em que uma exceção da camada abaixo é recebida é encaspulada por uma exceção compreendida pela camada acima.

A segunda forma é utilizada na última camada da aplicação. Se a aplicação é de servidor, normalmente a exceção é tranformada para uma mensagem e enviada à aplicação cliente. Por exemplo, em aplicações REST é convertido num código HTTP com algum JSON no corpo da mensagem que pode ser interpretado pela aplicação cliente.

Se a aplicação é um cliente gráfico, normalmente a exceção é transformada num aviso gráfico para o usuário. Normalmente uma mensagem em tela.

Implementação

A definição de um ExceptionHandler segue o padrão Service: uma interface, multiplas implementações. Cada interface é adaptada ao seu tipo de tratamento e camada onde será usada.

Para uma interface de encapsaulamento, seria algo como:

public interface ExceptionHandler {

 void handle(Throwable cause);

}

O método é void porque ele lançará a exceção que será criada pela implementação com base na causa passada.

Para uma interface de transformação, seria algo como:

public interface ExceptionHandler {

 ResponseEntity handle(Throwable cause);

}
Note
Aqui usamos a classe ResponseEntity como um exemplo possível de classe que contém a informação necessária para o REST, mas ha muitas outras opções conforme as suas decisões de arquitetura.

A implementação em si, é apenas um conjunto de `if`s que tentam decidir o tratamento adquado conforme o tipo da exceção. Em alguns casos atributos da exceção pode ser usados também.

public class RestExceptionHandler implements ExceptionHandler {

 ResponseEntity handle(Throwable cause){
     if (cause instancof IllegalArgumentException){
       return respondeWith(500, "Some internal problem")
     } else  if ( cause instancof NotauthenticatedException){
       return respondeWith(401, "Needs authentication")
     } else  if ( cause instancof NotAuthorizedException){
       return respondeWith(403, "Needs permissions")
     } else if ...
        // outros tipos
     }
     LOG.error(cause);
     return respondeWith(500, "Some internal problem")
  }
}

O tratamento adquado depende da sua aplicação. O importante notar aqui é que cada tipo de exceção é transformado para um código de retorno e uma mensagem. No fim, para os tipos de exceção não reconhecidados a exceção é registrada e um código geral é retornado. Este deve ser o único ponto do código onde a exceção é registrada.

Discussão

O padrão ExceptionHandler é bastante útil porque unifica e simplifica o tratamento de exceções na última camada da aplicação. Dependendo da aplicação podem ser necessários mais de um tipo de implementações do padrão. Vimos o tratamento para REST e teriamos o tratamento para mensagem em tela, mas pode acontecer se não podermos retornar nenhuma mensagem e apenas registrar o acontecido, por exemplo, no caso do código de uma tarefa agendada que é invocada pelo container.

Padrões associados

O padrão ExceptionHandler é uma forma de Service especializada em lidar com exceções e portanto pode ter diferentes interfaces e implementações. Tal como o Service instancias de ExceptionHandler devem ser injetadas onde necessário.

Os melhores frameworks de mercado normalmente dão suporte ao conceito de ExceptionHandler e têm a sua própria arquitetura de como lidar com exceções. Sempre revise a documentação do framework para entender como aplicar o padrão.

Scroll to Top