As anotações são um recurso disponível desde a versão 5 do Java. Tratam-se de metadados que não fazem parte do próprio programa e não tem efeito direto na sua execução. Mas então para quê elas são úteis? Podemos manipula-las em tempo de execução. Veremos como tirar vantagem disso numa situação problema.
Anotações podem ser manipuladas em tempo de execução
O Problema
Imagine a situação onde temos uma entidade chamada Customer e que precisamos registrar o histórico de alterações de determinados campos dessa tabela. Apenas os campos phone e address precisam ter o histórico. Precisaremos saber qual o campo alterado, o seu valor antigo e o seu novo valor.
A Solução
A nossa receita de solução envolve dois ingredientes importantes: anotações e a API Reflection do Java. Essa dupla dinâmica atuará da seguinte maneira: As anotações indicarão quais campos precisam ter o histórico registrado e a API Reflection nos permitirá verificar em tempo de execução a presença ou não dessas anotações nos campos. Mãos ao código!
Criando a anotação
Vamos começar definindo nossa própria anotação. Vamos chama-la de Auditable .
1 2 3 4 5 |
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Auditable { } |
Olha que interessante, utilizamos anotações para definir a nossa anotação. A anotação Target indica qual elemento do programa a nossa anotação pode ser aplicada. No nosso caso a anotação Auditable será aplicada em campos de classe. A anotação Retention recebendo o parâmetro RetentionPolicy.RUNTIME indica que ela estará disponível para ser acessada em tempo de execução que é o que vamos precisar.
Aplicando a anotação na classe
Com a nossa anotação criada é hora de utiliza-la. Como definimos ela será aplicada em campos da classe. Como queremos que apenas os campos address e phone armazenem seu histórico, colocamos a annotation somente para esses campos.
7 8 |
@Auditable String phone |
Usando a API Reflection
A API Reflection nos oferece o método isAnnotationPresent . O trecho de código abaixo é auto-explicativo: a instrução retorna verdadeiro se o campo phone é anotado com a anotação Auditable
1 |
Customer.getDeclaredField("phone").isAnnotationPresent(Auditable) |
Juntando tudo
No nosso método de update vamos utilizar a API Reflection do Java para verificar se os campos alterados possuem precisam armazenar o histórico de alterações.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public void update(Long customerId, Map params) { Customer customer = Customer.get(customerId) customer.properties = params //#1 List<Map> listOfUpdatedFields = getUpdatedFields(customer) customer.save(flush: true, failOnError: true) //#2 listOfUpdatedFields.removeAll{ !Customer.getDeclaredField(it.fieldName).isAnnotationPresent(Auditable) } //#3 for (Map updatedField : listOfUpdatedFields) { customerHistoryService.save(customer, updatedField.fieldName, updatedField.oldValue, updatedField.newValue) } } |
(#1) Armazeno uma lista de Map contendo o nome do campo, o valor antigo e o valor novo
(#2) Tirando vantagem do açúcar sintático do Groovy, nós removemos da lista todos os campos que não possuem a anotação.
(#3) Iteramos todos os campos e salvamos o histórico deles na base
Concluindo
Uma outra solução seria ao salvar um novo registro na nossa tabela Customer verificar se o campo alterado precisa ser registrado no histórico. Isso geraria uma implementação com estruturas condicionais que deixariam o código verboso.
As anotações podem nos ajudar a construir soluções elegantes para os nossos problemas do dia-a-dia. Elas também podem ser utilizadas pelo compilador para detectar erros ou por ferramentas para geração de código ou estruturas XML. O Java já possui anotações pré-definidas como o @Test do JUnit, o @Override , o @SuppresWarnings . Frameworks consolidados no desenvolvimento Java como Hibernate e Spring utilizam poderosamente das annotations. Lembra da @Entity do Hibernate?
Referências
- https://docs.oracle.com/javase/tutorial/java/annotations/basics.html
- http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/Target.html
- http://www.caelum.com.br/apostila-java-testes-xml-design-patterns/reflection-e-annotations/#9-5-usando-bem-anotacoes