Anúncio
Anúncio

Mais conteúdo relacionado

Anúncio

Último(20)

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
  2. Agenda Frameworks Annotations Reflection Framework Final: Serializador e desserializador XML-Objeto
  3. Discussão sobre Frameworks
  4. Discussão sobre Frameworks Hora de discutir! O que significa Framework?
  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)
  20. 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
  22. Annotations pré-existentes @Override @SuppressWarnings @Deprecated @Rentention @Target @Documented @Inherited
  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
  24. @SuppressWarnings Informa o compilador que determinado(s) warning(s) são de seu conhecimento e não devem ser emitidos pelo compilador. Exemplo 2
  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
  30. Annotation de nenhum parâmetro Exemplo 4
  31. Annotation de único parâmetro Exemplo 5
  32. Annotation de múltiplos parâmetros Exemplo 6
  33. Tipos de Parâmetros Tipos primitivos String Class Enum Array dos tipos acima Exemplo 7
  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?
  39. Reflection Vamos ver agora um exemplo simples desses três passo. Exemplo 11
  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>
  95. Contatos Email: fernando.camargo.ti@gmail.com Twitter: @fernandosst Facebook: www.facebook.com/fernandosst LinkedIn: br.linkedin.com/pub/fernando-camargo/ 26/21/286 Blog: fernandocamargoti.blogspot.com
Anúncio