Veja nesse artigo os primeiros passos no desenvolvimento de um sistema para gerenciamento de bibliotecas utilizando JSF, JPA e CDI.
Artigos Java Criando uma aplicação corporativa em Java – Parte 1
Por que eu devo ler este artigo:Nesse artigo será desenvolvida do zero uma aplicação para gerenciamento de bibliotecas, utilizando JSF, JPA e CDI, mostrando como integrar essas três tecnologias em um projeto.
Para isso, serão abordados recursos como o PhaseListener do JSF, as anotações @Inject, @Produces, @Disposes e @Interceptor relacionadas ao CDI, como criar e mapear entidades usando JPA, dentre outras coisas. Como servidor de aplicação, adotaremos o JBoss WildFly, destacando como configurá-lo. Além disso, adotaremos o Java 8 e alguns dos seus novos recursos, como a nova API para manipulação de datas.
Se olharmos para a plataforma e para a linguagem Java desde as primeiras versões, podemos notar uma enorme evolução. Com o passar dos anos foram surgindo diversas tecnologias, frameworks e especificações que fazem parte desse longo e contínuo processo evolutivo.
Nesse artigo iremos conhecer um pouco mais sobre três importantes especificações Java: JSF, JPA e CDI, e desenvolver uma aplicação que utilize implementações dessas especificações para vermos como funciona a integração entre elas.
Nesse contexto iremos entender porque surgiram, quais vantagens trazem e quais tipos de problemas resolvem.
A aplicação que será desenvolvida ao longo do artigo serve para fazer o gerenciamento de uma biblioteca. Nela poderão ser gerenciadas, por exemplo, as informações das editoras, dos livros, dos leitores, dos funcionários da biblioteca e dos empréstimos dos livros.
Especificações Java
Foram citadas três especificações Java no texto introdutório deste artigo, mas quem está começando a programar nesta linguagem pode não estar familiarizado com o termo "especificação". Então, o que seria isso?
Em meio ao processo evolucionário do Java, muitas vezes uma grande ideia acaba tendo mais de uma implementação. Foi justamente isso que aconteceu quando surgiu a proposta de se ter um framework ORM (Object-Relational Mapping).
Relacionadas a essa proposta vieram implementações como Hibernate, EclipseLink, OpenJPA, entre outras.
Para que exista uma padronização entre essas implementações é definida uma especificação, que nada mais é do que um documento que garante que todas elas tenham um comportamento igual ou bem semelhante e que contemplem as funcionalidades especificadas.
No caso dos frameworks ORM foi criada a especificação JPA. Dessa forma, se a implementação que está sendo usada em algum projeto começa a apresentar bugs ou é descontinuada, podemos trocá-la por outra sem muito esforço.
Às vezes, no entanto, as implementações surgem antes da especificação. Assim, para manter a padronização, as implementações passam por mudanças para se adequar ao que foi determinado. Um bom exemplo é o Hibernate, que surgiu antes da especificação JPA.
Para quem tiver interesse em saber mais sobre o processo de criação das especificações, recomendamos consultar o site do Java Community Process (JCP).
Sobre o JSF
O JSF pode ser definido como um framework MVC padrão para construir interfaces com o usuário baseadas em componentes para aplicações web.
A especificação JSF dita como deve funcionar esse framework. Deste modo, cada implementação do JSF, como o Mojarra e o MyFaces, agrega as funcionalidades do framework sempre indo ao encontro da especificação.
O JSF surgiu de uma necessidade de termos uma ferramenta mais simples, eficaz e produtiva no que sei refere à criação de interfaces gráficas para aplicações web. Atualmente, inclusive, conseguimos criar soluções web com recursos visuais complexos de forma simples ao adotar, por exemplo, o PrimeFaces, uma das bibliotecas de componentes mais famosas para JSF.
O PrimeFaces possui centenas de componentes ricos que podem ser adicionados facilmente à aplicação, porém nesse artigo não vamos utilizá-los, pois iremos nos ater aos componentes padrões do JSF.
Além de o JSF facilitar muito a criação da interface gráfica, ele provê uma maneira padrão de solucionar problemas comuns que encontramos durante o desenvolvimento de aplicações web, a saber: validações, conversões, criação de templates, etc.
Antes de chegarmos à origem do JSF, vamos voltar um pouco mais no tempo e olhar para as tecnologias que o antecederam.
A API de Servlets contribuiu bastante para a popularização do Java, sendo amplamente adotada no desenvolvimento de sistemas web. Hoje os Servlets funcionam "por baixo dos panos" nos frameworks mais novos, inclusive no JSF, e é parte importante na infraestrutura das aplicações web.
Anteriormente, no entanto, eram usadas diretamente para gerar dinamicamente as páginas de resposta às requisições dos usuários.
Como verificado com o tempo, essa forma de criar conteúdo dinâmico se mostrava propensa a erros e complicada, pois misturava código HTML com código Java.
Veja um exemplo de código que poderia aparecer em uma Servlet: out.println("<body text=\"black\" bgcolor=\"blue\" link=\"yellow\">");. Confuso, não?
Para piorar a situação, nesse exemplo e em muitas situações ainda temos que usar o caractere de "escape" barra invertida (\) para "escaparmos" das ocorrências de aspas duplas(").
O próximo passo na evolução das tecnologias Java web foi a introdução das JavaServer Pages. Essa tecnologia consistia em criar páginas com extensão .jsp que aceitavam tanto conteúdo HTML como porções de código Java (scriptlets, declarations e expressions).
Apesar da JavaServer Pages ter significado uma grande melhoria na forma de gerar conteúdo dinâmico para ser apresentado ao usuário, pois as páginas JSP eram praticamente páginas HTML com tags especiais para incluir código Java, ainda assim continuávamos com códigos HTML e Java misturados no mesmo arquivo.
Visando resolver esse problema surgiu a JSTL (Java Standard Tag Library) e a EL (Expression Language). Assim os códigos Java poderiam ser eliminados das páginas JSP.
Com a introdução da JSTL e da EL passou a ser possível implementar o MVC nas aplicações Java web e separar as responsabilidades de cada componente.
Sendo assim, classes Java comuns representavam a camada Model, as páginas JSP representavam a camada View e os Servlets representavam a camada Controller.
No entanto, apesar de naquela época implementar o MVC dessa maneira ter se tornado uma prática comum porque deixava as aplicações mais organizadas, ainda era muito trabalhoso desenvolver usando JSP e Servlets. Eis que surgem os frameworks MVC.
Ao falar de frameworks MVC, podemos dar destaque ao Struts, que por um bom tempo foi muito utilizado. Uma das razões pela grande adoção do Struts pela comunidade Java foi que ele resolvia de forma inteligente o problema relacionado a separar a aplicação em camadas.
Antes do Struts, com JSP e Servlets sem um framework, muitos desenvolvedores, principalmente os inexperientes, acabavam criando o mau hábito de colocar código Java na camada de visualização.
Já com o Struts, devido a sua arquitetura MVC, os desenvolvedores eram impelidos a separar o código de apresentação (View) do restante do código (Controller e Model).
Apesar da boa arquitetura desta até então excelente opção, ainda era muito trabalhoso e custoso construir as interfaces gráficas. Então foi criado o JSF, para que se tivesse um framework que também fosse MVC, mas que facilitasse o trabalho de criação da camada de apresentação.
Na época que o JSF surgiu o Struts era o framework mais utilizado, mas o JSF aos poucos foi ganhando seu espaço.
O profissional que está acostumado com outros frameworks como Struts ou Spring MVC, por exemplo, pode estranhar um pouco a forma como o JSF funciona, pois estes outros frameworks são considerados Action Based, enquanto o JSF é considerado Component Based.
No entanto, não se preocupe com isso, pois na parte prática do artigo será apresentado o "jeitão" JSF de ser.
Sobre a JPA
A especificação Java Persistence API, ou simplesmente JPA, foi criada para padronizar a forma como os frameworks ORM devem trabalhar. Essa especificação define uma série de anotações e interfaces, e os frameworks ORM devem implementar essas interfaces para que todos eles possam ser utilizados a partir de uma única API. A especificação também dita o que é esperado que cada método faça, e a implementação fica a cargo de cada provedor JPA – vide BOX 1.
BOX 1. Provedor JPA
Provedor JPA e persistence provider são termos usados para referenciar as implementações da especificação JPA, como Hibernate, EclipseLink, OpenJPA, dentre outras.
A seguir conheceremos alguns dos problemas que motivaram a criação dos frameworks de mapeamento objeto-relacional, que ao apresentarem as soluções para tais problemas facilitaram a vida dos programadores, elevando inclusive a produtividade durante o desenvolvimento.
Atualmente, a grande maioria das aplicações usa algum banco de dados relacional. No entanto, como veremos temos um pequeno problema nesse ponto. Quando falamos de bancos de dados relacionais, logo pensamos em tabelas, relacionamentos, chaves primárias, chaves estrangeiras, etc.
Já quando o assunto é linguagem orientada a objetos, logo pensamos em classes, composição, herança, polimorfismo, etc. Como conciliar essas diferenças?
Ao utilizarmos JDBC para converter o conteúdo de um ResultSet em objetos, conseguimos notar as similaridades entre uma classe e uma tabela, mas apesar das semelhanças existem também diversas diferenças entre o modelo OOP e o relacional.
Muitas vezes alguma coisa que existe no paradigma relacional não existe no orientado a objetos, e vice versa, o que remete a certa complexidade para fazer os dois modelos conversarem, exigindo que o desenvolver contorne essas diferenças programaticamente, fazendo para isso diversos ajustes no código.
A essas diferenças fundamentais entre os dois paradigmas são dadas o nome de Impedância (impedance mismatch).
Em Java, uma das formas de se trabalhar com bancos de dados é utilizar a API JDBC. Entretanto, um dos problemas ao utilizarmos JDBC é que precisamos escrever código SQL misturado com nosso código Java.
Assim, se for preciso alterar alguma instrução SQL, será necessário recompilar a classe na qual foi feita a alteração. Diante disso, alguns programadores adotaram estratégias como remover os códigos SQL da aplicação, movendo-os para um arquivo TXT ou XML. Nesse momento até surgiram frameworks, como o IBatis, para ajudar a realizar esse isolamento.
Outro problema que podemos enfrentar ao utilizar JDBC e escrever os SQLs diretamente em classes Java é verificado quando precisamos mudar de banco de dados.
Apesar de existir o padrão ANSI, instruções SQL podem apresentar diferenças significativas dependendo do SGBD utilizado, o que dificulta a tarefa de migrar de um banco para outro.
Se olharmos para as dificuldades e problemas descritos, podemos concluir que faria sentido se trabalhássemos apenas com a nossa linguagem de programação, usando somente o paradigma OOP, e se fosse abstraída a responsabilidade de escrever SQL ou fazer conversões entre o modelo orientado a objetos e o relacional.
Foi justamente para isso que foram criados os frameworks ORM, como o Hibernate, EclipseLink, Apache OpenJPA, dentre outros.
A sigla ORM significa Object-Relational Mapping, ou Mapeamento Objeto-Relacional em português, e como o nome sugere essa tecnologia viabiliza um mapeamento entre um modelo e outro, para que possa haver uma conversão automática entre os modelos OOP e relacional.
O objetivo é que utilizando esse tipo de framework não precisemos mais nos preocupar com a escrita de instruções SQL, passando a trabalhar somente com objetos.
A especificação CDI, cujo nome completo é Context and Dependency Injection for Java EE, padroniza o processo de injeção de dependências (em inglês Dependency Injection, ou simplesmente DI) dentro do Java.
Antes do CDI, existiam vários frameworks que realizavam injeção de dependências, como o Spring, JBoss Seam, Google Guice, PicoContainer, dentre outros, mas cada um deles funcionava de um jeito diferente e não existia uma forma padrão de fazer a injeção das dependências.
Essa especificação, inclusive, sofreu várias influências dos frameworks que já existiam, principalmente do JBoss Seam 2 e do Google Guice.
Uma vantagem no uso do CDI, se comparado ao uso do JBoss Seam 2, é que a injeção das dependências é feita de modo type safe, e no caso do Seam 2 é feita baseada em Strings.
Isso significa que utilizando CDI os beans a serem injetados são selecionados com base no tipo que é requerido no injection point e não com base no nome do bean, o que torna o processo de injeção mais seguro.
Injection Point é um ponto ou lugar, geralmente dentro de uma classe, no qual a dependência pode ser injetada.
A especificação CDI (JSR 299) se relaciona com a especificação Dependency Injection for Java (JSR 330) e uma complementa a outra. Essa relação se dá pelo fato de utilizarmos as anotações @Inject, @Named, @Qualifier, @Scope e @Singleton – definidas na JSR 330 – no processo de injeção de dependências. Em outras palavras, ao utilizarmos CDI acabamos fazendo uso dessas anotações.
Dependency Injection é uma das formas de se aplicar uma técnica muito mais ampla chamada Inversão de Controle (em inglês Inversion of Control, ou simplesmente IoC). A ideia principal da IoC é inverter o controle das chamadas dos métodos da aplicação em relação à programação convencional, ou seja, as chamadas não são determinadas diretamente pelo programador.
Alguns frameworks usam o conceito de inversão de controle e controlam as chamadas de métodos específicos, que no caso do Java geralmente são marcados com alguma anotação.
Quando usamos DI também ocorre inversão de controle, mas de uma forma um pouco diferente da mencionada anteriormente, pois nesse caso invertemos o controle da criação das dependências, que passa a não ser feita mais por nós, ou seja, já recebemos as dependências instanciadas e prontas para serem usadas.
A principal vantagem de se adotar a injeção de dependências é que esse design pattern diminui o acoplamento entre as classes e suas dependências.
Felizmente ao utilizar CDI acabamos empregando esse padrão de projeto sem mesmo perceber, de forma transparente, e podemos focar nas regras de negócio da aplicação que estamos desenvolvendo, deixando de lado a preocupação de como as dependências serão satisfeitas.
Para que o leitor entenda a importância de se diminuir o acoplamento entre as classes (e a implementação do CDI que escolhermos irá nos ajudar nisso) é preciso entender o significado de acoplamento. Então vamos revisar dois conceitos importantes na orientação a objetos, que são o acoplamento e a coesão.
A coesão está relacionada ao que uma classe sabe fazer. Se a classe foi criada de forma que tenha um único e bem focado propósito, dizemos que ela é coesa.
No entanto, se a classe sabe fazer muitas coisas, dizemos que ela é pouco coesa. A alta coesão é algo desejável, enquanto a baixa coesão é algo que devemos evitar.
O acoplamento, por sua vez, está relacionado com a forma como uma classe se relaciona e interage com as outras. Vejamos alguns exemplos para ajudar no entendimento. Se a cl ...