public interface ExceptionHandler {
void handle(Throwable cause);
}
Documentar e/ou simplificar o tratamento de exceções.
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
Encaspular uma exceção em outra
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.
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.
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.
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.