Altere argumentos recebidos na controladora

Proxy nunca foi tão simples com Spring AOP

Willian Antunes
Editora Globo
5 min readJul 16, 2018

--

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).

https://medium.com/@mithunsasidharan/understanding-the-proxy-design-pattern-5e63fe38052a

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!

Só não fume na sala de aula, por favor…

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.

--

--