Para acessar um método protected de uma superclasse usando reflection, precisamos desabilitar o mecanismo de verificação de visibilidade (acess check) antes de invocar o método. Isso pode ser feito chamando o método setAccessible(true) no objeto Method antes da invocação.
Construção de Frameworks com Annotation e Reflection API em Java
1. Frameworks com Annotations e
Reflection API
Fernando Camargo
Desenvolvedor Java EE e Android
Graduando do 4º ano de Engenharia De
Computação pela UFG
Estuda e programa em Java desde 2009
Em estudo para a OCJP 6
5. Discussão sobre Frameworks
”Um framework, em desenvolvimento de
software, é uma abstração que une códigos
comuns entre vários projetos de software
provendo uma funcionalidade genérica.”
6. Exemplo
Uma empresa de tecnologia, ao longo dos anos,
foi desenvolvendo diversos sistemas para uso
interno. Após anos, uma regra de negócio foi
alterada para melhor posicionar a empresa no
mercado. Essa regra era implementada em
todos os sistemas internos. E agora, como
alterá-la?
7. Framework e biblioteca
Biblioteca:
Classes independentes usadas para
determinadas funções (utilitários) que são
chamadas pelo cliente.
Framework:
Interconexão de classes que trabalham em
conjunto para um fim comum às várias
aplicações. O cliente insere código específico
que será chamado pelo código geral do
Framework.
8. Exemplo
Depois de um projeto de 3 anos, um grupo de
programadores pega um projeto parecido e
com grande nível de complexidade. Vendo que
poderiam reutilizar código, eles copiam e colam
o que pode ser reusado nesse novo projeto.
Depois de 1 ano, eles descobrem que há uma
falha grave de segurança no primeiro projeto,
em código comum ao 2º. Como resolver o
problema dos dois? Se esse código reusável
estivesse em um único lugar, seria mais fácil
ou difícil?
9. Framework
Acima de tudo, reusável
Atender as novas classes criadas, fazendo
coisas específicas.
Como ser reusável e ser específico ao mesmo
tempo? Não possuimos nada sobre as classes
que serão criadas no futuro!
10. Prever o Futuro (?)
Existem algumas formas de prevermos o futuro,
nesse caso.
Interfaces
Classes abstratas
Metadados
11. Interfaces e Classes Abstratas
Através delas, podemos prever o que poderá ser
feito em tempo de compilação, mas obrigamos
o cliente a assinar contratos (interfaces),
implementando métodos que podem não ser
de seu interesse ou tiramos sua oportunidade
de usar herança, obrigando-o a fazer sua
classe herdar de uma classe abstrata.
12. Interfaces e Classes Abstratas
Interfaces e classes abstratas podem compor um
framework?
Sim.
Então por que não usar apenas interfaces e
classes abstratas?
Plug and play
13. Alternativa
Alternativa para deixar as classes de negócios
simples e independentes:
Metadados: XML e Annotations
Interpretação e execução: Reflection API
14. Metadados
Dados que descrevem dados
Mostra ao framework o que você deseja
Deixa as classes limpas para implementar
apenas o necessário
Desvantagens: não geram erros de compilação,
já que as verificações só poderão ser feitas em
tempo de execução.
15. Metadados (XML)
Pode ser criado para configurar um framework e
lhe mostrar a direção correta.
Vantagem: pode ser modificado sem necessitar
recompilação.
Desvantagem: aplicações grandes e complexas
podem gerar arquivos XML gigantes e difíceis
de serem lidos.
16. Metadados (Annotations)
São anotações que descrevem classes,
métodos, propriedades e parâmetros.
São anotadas em sua própria classe, ao invés de
acumuladas em um XML.
Facilita a leitura e o ententimento de outros
programadores
Desvantagens: necessita recompilação do
código, quando alterado.
17. Reflection
Reflection:
É a habilidade de um programa observar e
modificar sua própria estrutura em tempo de
execução.
Java provê essa habilidade através da Reflection
API
18. Interpretando os metadados
Com Reflection, podemos interpretar os
metadados que descrevem uma classe e
acessar, modificar e chamar propriedades,
métodos, e toda a estrutura da classe.
O código interpreta os metadados e as classes
descritas em tempo de execução.
19. Frameworks famosos
Spring (XML, Annotations e Reflection)
EJB 3 (XML, Annotations e Reflection)
JSF (XML, Annotations e Reflection)
Hibernate (XML, Annotations e Reflections)
JUnit (Classes Abstratas → Annotations)
21. Introdução às Annotations
Criadas na versão 1.5 do Java
Têm papel de metadado
Podem descrever:
Classe
Construtor
Método
Parâmetro
Variável local e de instância
Annotation
23. @Override
Informa o compilador que determinado método
está sendo sobrescrito.
Caso anote um método que não é herdado,
ocorrerá um erro de compilação.
Exemplo 1
25. @Deprecated
Avisa aos possíveis usuários da API e o
compilador que aquele método não deveria ser
mais usado, pois há alternativas melhores.
Exemplo 3
26. As demais
@Rentention, @Target, @Documented e
@Inherited são meta-annotations. Usadas para
descrever outras annotations e será mostrado
em breve.
27. Annotations criadas por
Frameworks famosos
@Entity: Descreve uma classe como uma
unidade a ser persistida.
@ManagedBean: Descreve que uma classe
deve ser gerenciada pela JSF.
@Named: Nomeia uma classe para ser usada
em CDI.
@Service: Descreve uma classe como um
serviço.
@Inject: Descreve que determinada instância ou
parâmetro deve ser injetada.
28. Criando nossas Annotations
Uma Annotation é declarada da seguinte forma:
public @interface Annotation{
String parametro();
}
29. Classificações de Annotations
Podemos separar as Annotations em várias
classificações:
Quanto a quantidade de parâmetros
Quanto ao seu alvo
Quanto a sua retenção
Documentadas ou não
34. Alvos das Annotations
As annotations, quando criadas, podem definir
em quais lugares podem ser aplicadas.
A meta-annotation @Target indica quais
elementos de código para os quais uma
annotation é aplicavel.
Esses alvos são: Tipo, Propriedade, Método,
Parâmetro, Construtor, Variável Local e
Annotation.
Exemplo 8
35. Retenção das Annotations
As annotations também podem definer até que
ponto elas devem ser consideradas.
Através da meta-annotation @Retention,
podemos definir que uma annotation deva ser
considerada. Temos três opções:
Apenas no código fonte.
Durante a compilação
Durante a execução (usada no Reflection)
Exemplo 9
36. Documentação da Annotation
As annotations podem ou não aparecer no
JavaDoc do projeto. Para habilitar isso, apenas
anote a Annotation com:
@Documented
Lembre-se: Um framework não documentado é
inútil!
37. A meta-annotation @Inherited
Indica que a annotation criada no momento será
herdada em todas as subclasses.
Ou seja, quando buscarmos se uma determinada
classe tem essa annotation, a encontraremos
caso uma superclasse a tenha.
Exemplo 10
38. Reflection
Examinar o programa por sua estrutura e dados
Tomar decisões usando os resultados do exame
Mudar o comportamento, estrutura ou dados do
programa baseado nas decisões
O trabalho de um programador? Ou pode ser
feito pelo próprio programa?
40. Reflection - Introspection
Reflection é a habilidade de um programa fazer
os 3 itens ditos anteriormente.
Para fazer um auto-exame, é necessário que o
programa tenha uma representação de si
mesmo, o que chamamos metadados. Na
orientação a objetos, metadata é organizada
em objetos, o que nos dá metaobjetos. A auto-
examinação de um programa em tempo de
execução é chamada introspection
(introspecção).
41. Reflection – Mudança de
comportamento
Existem três técnicas para fazer a mudança de
comportamento em um programa:
Modificação direta do metaobjeto
Operações usando os metadados
Intercessão (interceder em várias fases de
execução do programa)
Java nos traz a Reflection API várias operações
usando os metadados e algumas capacidades
de intercessão, mas não nos permite modificar
o metaobjeto.
42. Discussão
Imagine a situação em que um projeto tenha
várias classes de diferentes bibliotecas que
compoem a interface gráfica. Algumas dessas
bibliotecas não podem ser modificadas.Todas
elas possuem um método setColor, que recebe
um objeto do tipo Color, mas elas não
implementam nenhuma interface em comum
nem extendem uma superclasse.Queremos
chamar esse método para qualquer desses
componentes, o que faremos?
43. Melhor solução para a Discussão
A melhor solução para esse problema é um
método que recebe um Object e faz uso de
Reflection para encontrar um método setColor
que receba um parâmetro Color e o chamar.
Exemplo 12
44. Class
O objeto do tipo Class é a base da introspecção.
Ele é o metaobjeto que representa determinada
classe.
Através dele acessamos:
Campos
Métodos
Árvore hierárquica (superclasses e subclasses)
Construtores
Annotations
45. Obtendo um objeto Class
Temos três formas de obter o objeto Class.
getClass(): Herdade de Object. Nos retorna, em
tempo de execução, o objeto Class de uma
determinada instância.
.class: É chamado usando o nome da classe
seguido por '.class'. Devemos decidir em tempo
de compilação qual classe queremos.
Class.forName(”className”): Retorna o objeto
Class a partir do nome da classe.
46. Obtendo um método
Um método tem por assinatura seu nome e seus
parâmetros. Podemos obtê-los da seguinte
forma:
getMethod: retorna um método público com dada
assinatura, independente se é herdado ou
declarado na classe.
getDeclaredMethod: retorna um método
declarado na classe, independente de sua
visibilidade, a partir de sua assinatura.
47. Assinatura de um método
Ambos getMethod e getDeclaredMethod
recebem os mesmos parâmetros:
getMethod(String nomeDoMetodo,
Class... parametros);
Os parâmetros são um var-args de Class que
representam esses parâmetros.
48. Listando métodos
Podemos listar todos os métodos de uma classe
através dos métodos:
getMethods(): retorna um array de Method de
visibilidade pública, declarados ou herdados.
getDeclaredMethods(): retorna um array de
Method declarados na classe, independente da
visibilidade.
49. Discussão
Vimos que passamos um var-args de Class para
encontrarmos um método.
Como encontrariamos um método que recebe
como parâmetro uma variável primitiva, uma
interface e/ou um array?
50. Representando tipos através de
Class
Em Java, devido ao problema da discussão,
todos os tipos são representados com um
objeto Class. Isso inclui as Interfaces, os tipos
primitivos e os arrays.
Esses objetos Class não podem fazer todas as
coisas que outras podem. Você não pode criar
uma nova instância de um primitivo ou uma
interface.
Essas representações são necessária para fazer
a introspecção.
51. Representando tipos através de
Class
Após obtermos o objeto Class, podemos testar
se ele representa um array, um primitivo ou
uma interface através dos métodos:
isArray()
isPrimitive()
isInterface()
Veremos agora como Java representa cada um
desses tipos usando objetos Class.
52. Representando tipos primitivos
Embora tipos primitivos não sejam objetos, Java
usa objetos Class para representar todos os 8
tipos primitivos. Os primitivos são
representados usando o literal 'class' após o
nome do primitivo. Assim, representamos:
int.class, float.class, double.class, short.class,
long.class, char.class, boolean.class, byte.class
Também precisamos de uma representação para
void, que é representado por void.class.
53. Representando interfaces
Java também introduz um objeto Class para
representar cada Interface declarada. Esses
objetos podem ser usados para indicar
parâmetros do tipo de uma interface.
Obtemos o objeto Class que representa uma
interface através do literal 'class'. Um exemplo
seria a interface Collection, sendo
representada por Collection.class.
54. Representando arrays
Arrays, em Java, são objetos, mas suas classes
são criadas pela JVM em tempo de execução.
Seus objetos Class também são obtidos
através do literal 'class'. Por exemplo, um array
de uma dimensão de Object é representado
por Object[].class.
Podemos descobrir qual o componente de um
array através de getComponentType(), que
retornaria um objeto Class de Object para o
exemplo acima.
55. Exemplo: fazendo introspecção
na classe Vector
Vamos fazer introspecção para pegar os
seguintes métodos da classe Vector:
addAll(Collection c)
copyInto(Object[] anArray)
get(int index)
Exemplo 13
56. O objeto Method
Representa um método de determinada classe
que tenha determinada assinatura (nome +
parâmetros).
Provê imformações sobre um método, como seu
nome, os tipos de seus parâmetros, o tipo de
seu retorno e exceções. Também dá a
habilidade de invocá-lo em uma instância da
classe que o declara.
57. Invocação dinâmica (1)
Habilita um programa a chamar um método de
um objeto em tempo de execução sem
especificá-lo em tempo de compilação.
Object invoke(Object target, Object...
parameters);
Target é o objeto que terá seu método chamada.
Parâmetros (var-args) são os parâmetros que o
método invocado espera. O método invoke
retorna um Object, o retorno do método
invocado.
58. Invocação dinâmica (2)
O primeiro parâmetro, target, será
desconsiderado em um método static, então é
legal passar null. Também é legal passar null
ou um nada em um método sem parâmetros.
Os parâmetros são passados como Object, o
que significa que são passados os Wrappers
ao invés dos primitivos (Integer – int). O
mesmo vale para o retorno, que retorna um
Object.
Um método de retorno void terá um retorno null
no método invoke.
59. Invocação dinâmica (3)
IllegalArgumentoException será lançada quando
o target não suportar o método ou quando os
parâmetros estiverem incorretos.
Qualquer exceção lançada dentro do método
invocado lançará uma
InvocationTargetException.
Exemplo 14
60. Discussão
Temos o método getMethod para acessar
métodos públicos (declarados e herdados) e
temos getDeclaredMethod para acessar
métodos declarados de qualquer visibilidade.
Como podemos acessar um método protected de
uma superclasse?
Exemplo 15
61. Árvore de hierarquia
Podemos ter acesso à informações da árvore de
hierarquia de uma classe através dos seguintes
métodos:
Class[] getInterfaces()
Class getSuperclass()
boolean isAssignableFrom(Class cls): Verificada
se cls é uma subclasse.
boolean isInstance(Object obj): Versão de
Reflection de instanteof.
62. O objeto Field
Representa um campo (variável de instância) de
determinada classe. É identificado pela classe
o qual pertence e pelo nome.
Através dele, obtemos informações sobre o tipo,
o nome, os modificador e até o valor do campo
de uma instância. Podemos usá-lo para pegar
e modificar o valor de um campo.
63. Acesso um objeto Field (1)
Assim como fizemos com o objeto Method, será
forma com o objeto Field.
getField(String nome): retorna um campo público
que tenha esse nome.
getDeclaredField(String nome): retorna um
campo de qualquer visibilidade declarado na
classe.
getFields(): retorna todos os campos públicos.
getDeclaredFields():retorna todos os campos
declarados.
64. Acesso a um objeto Field (2)
Para acessar todos os campos declarados e
herdados independente de visibilidade,
devemos percorrer a árvore hierarquica em
busca dos campos declarados nela.
Quando uma busca por um campo não o
encontra, é lançada a NoSuchFieldException.
Exemplo 16
65. Modificadores
Class, Method e Field possuem modificadores.
Entre seus possíveis modificadores, podemos
citar: public, private, protected, static, final,
abstract, etc.
Para termos acesso a esses modificadores,
usamos getModifiers(), que retorna um número
inteiro (vetor de bits). Para usarmos esse vetor
de bits, usamos a classe Modifier para
interpretá-lo.
Exemplo 17
66. Valor de um Field (1)
Podemos acessar o valor do campo de um
objeto e também modificá-lo através de
Reflection.
get(Object target): Retorna o valor do campo na
instância target.
set(Object target, Object value): Ajusta o campo,
na instância target, para o novo valor 'value'.
Em ambos os casos, primitivos são usados
através de seus Wrappers (int – Integer)
67. Valor de um Field (2)
Também há um método específico para cada
primitivo/wrapper. Porém, só devem ser usados
caso tenha certeza do tipo do campo, ou
lançaram IllegalArgumentException.
getBoolean / setBoolean
getInt / setInt
…
Exemplo 18
68. Acessibilidade (1)
Como visto no exemplo anterior, ao tentar
acessar campos não públicos, é lançada uma
IllegalAccessException. O mesmo acontece
com métodos.
Isso acontece porque, em tempo de execução,
há uma checagem de acesso na invocação de
métodos e acesso do valor de campos.
69. Acessibilidade (2)
Podemos contornar isso desabilitando a
verificação de acessibilidade em tempo de
execução. Fazemos isso usando:
setAccessible(boolean param): Se true, torna o
método/campo acessível via Reflection. Se
false, torna inacessível.
boolean isAccessible(): Verifica a situação atual
do método/campo.
Exemplo 19
70. Desvantagens na acessibilidade
Quebra o encapsulamento dos objetos.
Não é uma operação ”leve”.
Pode ser desabilitada em algumas JVMs.
Devemos usar essa operação apenas em caso
estritamente necessários. Ao contrário,
devemos priorizar por chamar apenas métodos
públicos e usar os getters e setters de
determinado campo.
71. Campo do tipo Array (1)
Array é um tipo de objeto diferente que merece
um tratamento especial. Para isso, o Java
provê a classe Array como um facilitador nas
operações com Array's em Reflection. Ele
possui os seguintes métodos:
getLenght(Object array): Retorna o tamanho do
array.
get(Object array, int i): Retorna o i-ésimo
elemento do array.
set(Object array, int i, Object valor): Ajusta o
valor do i-ésimo elemento do array.
72. Campo do tipo Array (2)
newInstance(Class tipo, int length): Cria um novo
array do tipo especificado com o tamanho
especificado.
Também temos outros métodos que devem ser
consultados na API.
Exemplo 20
73. Reflection e Generics (1)
Generics são usados em duas situações:
Declarar uma classe/interface como
parametrizada.
Usar uma classe parametrizada.
Usamos constantemente classes parametrizadas
desde o Java 5. Um grande uso está nas
collections, como List, Vector, Map, etc. Essa
parametrização nos permite criar uma lista de
String's ao invés de uma lista de Object's.
74. Reflection e Generics (2)
Em tempo de execução, essa parametrização é
apagada da classe, já que podem haver várias
instâncias com parâmetros diferentes. Mas
essa informação fica disponível nos Method's,
Field's, nas interfaces e superclasses
parametrizadas através de Reflection.
Assim como é importante saber o tipo dos
componentes de um Array, também é
importante saber o tipo dos objetos guardados
em uma Collection nas nossas introspecções.
75. Reflection e Generics (3)
É importante saber que Class implementa a
interface Type, pois trabalharemos com essa
interface e também com ParameterizedType.
Através de um Method ou um Field, teremos
acesso a um ParameterizedType (que pode
possuir mais de um tipo parametrizado).
Através disso, acessamos a lista de Type's,
que podemos dar um cast para Class e
continuar nossa introspecção.
76. GenericReturnType
Um método pode retornar uma classe/interface
parametrizada. É importante sabermos qual o
tipo dessa parametrização.
Usamos o método:
Type getGenericReturnType()
Esse método retorna um Type. Então podemos
testar usando 'instanceof' se esse tipo se trata
de um ParameterizedType. Caso seja,
acessamos os tipos como foi descrito.
Exemplo 21
77. Reflection e Generics (4)
É importante saber que todos os métodos
”getGeneric[...]()” retorna um Type, que pode
ser um ParameterizedType.
Esses métodos estão presentes em Method:
getGenericReturnType(): Usado para seu
retorno.
getGenericParameterTypes(): Retorna um array
de Type dos parâmetros.
getGenericExceptionTypes(): Funciona da
mesma forma para Exceptions.
78. Reflection e Generics (5)
Em Field:
getGenericType(): Usado para verificar o tipo do
campo.
Em Class:
getGenericInterfaces(): Retorna os tipos das
interfaces implementadas pela classe.
getGenericSuperclass(): Retorna o tipo da
superclasse.
79. Constructor (1)
Uma classe possui de 1 a N construtores. É
através deles que um objeto é construído.
Acessando um metaobjeto Constructor, podemos
construir um objeto de uma determinada
classe.
Podemos obter um Constructor da seguinte
forma:
Constructor getConstructor(Class...
tiposParametros): Retorna um Constructor para
os tipos de parâmetro especificados.
80. Constructor (2)
Constructor[] getConstructors(): Retorna todos os
construtores da classe.
Quando um construtor não é encontrado para
determinados tipos de parâmetros, é lançada
uma NoSuchMethodException, assim como
acontece quando um método não é
encontrado.
Também acessamos os tipos de seus
parâmetros através de:
getParameterTypes(): Retorna as classes que
representam os tipos dos parâmetros.
81. Constructor (3)
Possuindo uma instância do tipo Constructor,
podemos criar uma nova instância da classe a
qual ele pertence.
Criamos uma nova instância da classe da
seguinte forma:
Object newInstance(Object... parametros): Cria
uma nova instância da classe usando os
parâmetros passados.
Exemplo 22
82. Constructor (4)
As exceções lançadas e a acessibilidade do
construtor se assemelha ao de um método.
Também é válido torná-lo acessível através de
setAccessible(boolean).
Podemos parametrizá-lo com a classe que será
construíza, caso saibamos qual é. Assim, ao
invés de retornar um Object, ele retornará a
classe parametrizada.
83. JavaBeans e Reflection (1)
JavaBeans são POJOS (Plain Old Java Objects)
que serem um padrão de encapsulamento dos
campos.
Entre vários outras coisas especificadas, temos
que os campos devem ser privados com
métodos de acesso (get e set).
Como vimos, devemos evitar mudar a
acessibilidade dos campos com Reflection, por
ser um processo pesado e romper seu
encapsulamento.
84. JavaBeans e Reflection (2)
Padrão de métodos de acesso para campos
privados:
Propriedade: T xyz
public T getXyz()
public void setXyz(T novoValor)
Propriedade: boolean xyz
public boolean isXyz()
public void setXyz(boolean novoValor)
85. JavaBeans e Reflection (3)
Devemos, sempre que possível, recorrer aos
métodos de acesso para acessar campos
privados. Temos duas alternativas para isso:
Tratar o nome do campo para se chegar o nome
correto do método de acesso.
Utilizar as classes do pacote java.beans.
Vamos abordar a primeira alternativa por ser
mais simples. O pacote java.beans poderá ser
estudado depois.
Exemplo 23
86. Annotations e Reflection (1)
As Annotations especificadas a perdurarem até o
tempo de execução poderão ser acessadas via
Reflection.
Assim como classes, interfaces e primitivos, as
Annotations também tem uma representação
através de uma Class.
Uma instância com os valores definidos na
anotação é representado pelo tipo Annotation.
Podemos usar sua representação de Class
para alcançar sua representação Annotation.
87. Annotations e Reflection (2)
Classes, interfaces, métodos, parâmetros,
construtores e campos podem ser anotados.
Dessa forma, todos possuem os métodos:
Annotation getAnnotation(Class cls): Retorna
uma instância do tipo da Annotation. Podemos
dar um downcast para nossa annotation e usá-
la para acessar os valores definidos onde foi
anotada.
88. Annotations e Reflection (3)
Annotation[] getAnnotations(): Retorna um array
com todas as annotations definidas e herdadas
no elemento.
Annotation[] getDeclaredAnnotations(): Retorna
um array com apenas as annotations definidas
no elemento. As herdadas (@Inherited) são
ignoradas.
Exemplo 24
89. Por onde seguir
Antes de apresentar o Framework final, feito com
o conteúdo apresentado, segue uma lista do
que falta estudar:
Pacote java.beans.
Proxy (interceptação de métodos)
Questões de performance
Classloader
Geração de código
90. Framework Final (1)
A proposta de framework a ser implementada é a
seguinte:
Framework reponsável por gerenciar a
sincronização de dados entre diferentes
plataformas. Essa sincronização precisará de
trocas de mensagens, que irão conter uma
serialização dos objetos na forma de XML.
As diferentes plataformas que receberem as
mensagens terão um deserializador que
transformará o XML de volta para um objeto.
91. Framework Final (2)
Usando Annotations e Reflection, nos
limitaremos a implementar apenas o
serializador e o deserializador de Objeto-XML.
As classes dos objetos serão anotadas com
algumas configurações para a serialização.
Devemos criar esses Annotations e os métodos
de serialização e deserialização.
92. Especificando os metadados
Os metadados serão compostos das seguintes
annotations:
@XML: Marca que uma classe pode ser
serializada e deserializada em XML. Também
especifica como será a serialização dos
campos ou de um campo campo específico
@Transient: Marca que um campo não deve ser
serializado
@ServerId: Marca um campo como sendo o
identificador do objeto no servidor
93. Eagle Sync
A proposta do Framework está implementada
com código fonte livre sobre licença Apache
2.0, o que dá o direito de qualquer pessoa usar
o código da forma que quiser.
Para ver ou contribuir com o projeto, acesse:
code.google.com/p/eagle-sync/
94. Referências
FORMAN, I. R.; FORMAN, N. Java Reflection in
Action. Manning, 2006.
<http://www.developer.com/java/other/article.php/
3556176/An-Introduction-to-Java-
Annotations.htm>
<http://tutorials.jenkov.com/java-
reflection/index.html>
<http://www.dsc.ufcg.edu.br/~jacques/cursos/ma
p/html/frame/oque.htm>