Altere argumentos recebidos na controladora
Proxy nunca foi tão simples com Spring AOP
Tem situações que você cria uma, duas, três classes de negócio, e todas compartilham uma regra em comum que é executada no começo e no fim de cada método principal, ou quando você já tem uma instância de um objeto e precisa espia-lo para saber se um determinado método foi executado ou não (um salve para o Mockito) quando um fluxo é iniciado, enfim, são situações que se encaixam perfeitamente no design pattern conhecido como Proxy!
É algo até antigo, mas se você está trilhando agora seu caminho no mundo da programação ou já é um tiozão e não tenha passado por uma situação do tipo, saiba que existe uma mágica muito legal para criar Proxies de uma maneira muito elegante com Spring AOP.
Imaginem a situação:
Você tem um método que recebe um JSON via header e como não tem codificação o padrão é associado (ISO-8859–1). A sua controladora recebe tudo bagunçado já que quem enviou inseriu textos com acentos e afins, e pra piorar, o parâmetro é usado em uma função lambda, portanto não é possível alterá-lo de bate-pronto pois deve ser final ou efetivamente final.
O JSON antes de enviar para a nossa controladora:
{
"honest-parameter": "Por quê? É por que o porquê das coisas são estudadas. Estudo porque é importante"
}
O JSON após recebermos na controladora:
{ "honest-parameter": "Por quê? à por que o porquê das coisas são estudadas. Estudo porque é importante" }
Bom, existem algumas maneiras de resolver essa situação, uma implica em mudar a lógica atual e a outra em literalmente mante-la, porém embrulhando a execução do método para que tenha uma espécie de porteiro na entrada e na saída para inspecionar e/ou alterar aquilo que vai pra dentro (INPUT) e para fora (OUTPUT ou o tipo de dado retornado pelo método).
Mas cara, você não poderia criar um filtro HTTP?
Sim, com certeza, é uma possibilidade (veja detalhes aqui), mas temos 2 problemas:
- Em vez de trabalharmos com abstração do código Java, na verdade estaremos mexendo com o protocolo HTTP (requisição e resposta).
- Se por ventura precisarmos aplicar um filtro em uma classe de negócio e não em uma controladora, não seria possível, principalmente se a sua execução for fora do protocolo HTTP.
Portanto a melhor maneira é usar algo agnóstico, algo que não esteja vinculado a alguma regra, no caso nosso, um Proxy honesto!
Uma olhada rápida no código
O objetivo aqui não é explicar Spring AOP nas minúcias, até por que a documentação do Spring já está super completa! Mas só para termos noção, eis aqui o controladora:
Veja que o header é recebido como parâmetro, porém não posso alterá-lo pois qualquer tentativa o compilador reclamará o variable used in lambda expression should be final or effectively final
. Então eis o proxy que embrulha a chamada:
Usei a declaração Around pois com ela posso executar coisas antes e depois da execução do método, alterar seu retorno e até cancelar sua chamada se precisar, vai depender da regra de negócio adotada.
Vamos ao teste!
Temos um projeto de teste no repositório abaixo no GitHub. É importante mencionar que foi feito com Java 10, Spring Boot 2.1.0.BUILD-SNAPSHOT e JUnit 5. Para rodar os testes unitários tive que fazer uma manobra para executar com classpath em vez de modulepath.
Siga as instruções do README projeto para rodar e saber mais sobre a manobra. Quando estiver rodando, se você acessar /sample
sem o header exigido, acontece um erro:
Para simular o header, usei o plugin ModHeader para o Chrome. Fique a vontade em escolher um seu. Exemplo de configuração:
Depois de configurado é só acessar /sample
de novo e ver a conversão funcionando:
Como foi recebido no Proxy:
2018–07–14 13:04:55.521 DEBUG [http-nio-8080-exec-4] [b.c.g.c.a.SampleControllerAspect] What was intercepted: { “honest-parameter”: “Por quê? à por que o porquê das coisas são estudadas. Estudo porque é importante” }
E como foi recebido na controladora:
2018–07–14 13:04:55.527 INFO [http-nio-8080-exec-4] [b.c.g.c.SampleController] What was received in h-sample-header: { “honest-parameter”: “Por quê? É por que o porquê das coisas são estudadas. Estudo porque é importante” }
Não é algo trivial e também é custoso
É sempre importante lembrar que por embrulharmos o trecho de código real em um proxy traz uma certo overhead, na maioria das vezes é mínimo mas em sistemas críticos pode ser muito custoso. Use sempre com sabedoria!
Ao som de Seul, Fly Like An Eagle.