terça-feira, 4 de novembro de 2008

Spring Framework Java

O objetivo deste artigo é discutir e apresentar uma aplicação desenvolvida em Java adotando o framework Spring. Inicialmente, serão abordados alguns detalhes técnicos, principalmente sobre as tecnologias e padrões adotados pelo Spring. Posteriormente, então, uma aplicação será desenvolvida, visando apresentar, na prática, a adoção deste framework.

Introdução

Para obter um entendimento completo sobre as características do Spring, faz-se necessário compreender, inicialmente, o padrão Inversion of Control (Inversão de Controle), e sua variação denominada Dependency Injection (Inclusão de Dependência). Martin Fowler, em seu artigo entitulado Inversion of Control, leva a um entendimento mais aprofundado sobre este padrão, entretanto, é importante abordar aqui alguns detalhes.

A inversão de dependência é o que torna uma simples biblioteca de classes diferente de um framework. Uma biblioteca consiste em um conjunto de classes que um usuário instancia e utiliza seus métodos. Após a chamada ao método, o controle do fluxo da aplicação retorna para o usuário. Entretanto, em um framework este fluxo é diferente. Para utilizar um framework, código próprio da aplicação deve ser criado e mantido acessível ao framework, podendo ser através de classes que estendem classes do próprio framework. O framework, então, realiza a chamada deste código da aplicação. Após a utilização do código da aplicação, o fluxo retorna para ele.

Martin Fowler exemplifica este conceito através de interfaces de interação com o usuário (GUI). Em Swing, por exemplo, você define os tratadores de eventos para os vários campos da tela, enquanto o framework (Swing) contém o loop principal da aplicação.

Finalizando, temos ainda o padrão Dependency Injection, idealizado por Martin Fowler, que trata-se de uma especialização do padrão Inversion of Control. Aplicações como Spring e PicoContainer, denominados de lightweight containers, adotam a inversão de controle, entretanto, todo framework utiliza-se de inversão de controle. A pergunta é, então, que tipo de inversão de controle o Spring, por exemplo, realiza? Afirmar que o Spring é um bom framework porque aplica a inversão de controle é um erro, já que qualquer framework deve aplicar este padrão. Para compreender melhor o padrão dependency injection, partiremos para exemplos mais práticos.

A Figura 01 apresenta um modelo, no qual pode-se entender melhor este padrão. Observe a interface MovieFinder, responsável em definir o comportamento padrão para classes que desejam gerenciar um cadastro de filmes. Logo abaixo encontra-se a classe MovieFinderImpl, que define uma implementação concreta da interface MovieFinder. Nesta classe encontram-se as implementações para cada método definido na interface MovieFinder. Têm-se, ainda, a classe MovieLister, que utiliza uma implementação da interface MovieFinder para realizar a busca de filmes para apresentar em um tocador(player) de vídeo. Observa-se, neste exemplo, a dependência existente entre MovieLister e MovieFinder. Esta dependência é resolvida pela classe Assembler, a qual gerencia a “injeção” de uma implementação de MovieFinder, neste exemplo a classe MovieFinderImpl, em um objeto MovieLister. Portanto, este padrão trata da inversão sobre como eles procuram por uma implementação de uma interface para resolver a dependência entre os objetos.

Caso a classe MovieLister instancie diretamente (através da chamada de new MovieFinderImpl) um objeto do tipo MovieFinder, perderemos a capacidade de tornar MovieFinder “plugável”. A interface MovieFinder é, portanto, um contrato ou um padrão a ser seguido por quem deseja criar “Buscadores” de filmes, de forma que um módulo separado, o Assembler, possa injetar esta implementação em MovieLister. Podemos, desta forma, criar programas no qual as partes que o compõem são plugins gerenciados pelo Assembler.

Text Box:    Figura 1 - Exemplo de Dependency Injection

Basicamente, existem dois tipos de injeção de dependência: Constructor Injection e Setter Injection. No primeiro tipo, Constructor Injection, a dependência é resolvida através de um construtor do objeto a receber o objeto dependente.

public class MovieLister {
public MovieLister(MovieFinder finder) {
this.finder = finder;
}

}

Listagem 01

A Listagem 01 apresenta este tipo de injeção de dependência, na qual a classe MovieLister define como parâmetro do seu construtor padrão um objeto do tipo MovieFinder. Neste contexto, o objeto Assembler resolverá a dependência entre os dois objetos passando para MovieLister uma implementação concreta de MovieFinder através do seu construtor.

class MovieLister {
private MovieFinder finder;

public void setFinder(MovieFinder finder) {
this.finder = finder;
}

}

Listagem 02

A Listagem 02, por sua vez, apresenta o tipo Setter Injection, no qual a dependência entre os objetos é resolvida pelo Assembler através de um método Setter no objeto MovieFinder.

Concluindo, a adoção destes dois padrões visa permitir ao desenvolvedor focalizar-se na implementação das características específicas da aplicação, delegando para um framework, como o Spring, a tarefa de especificar a dependência entre alguns objetos.

Spring Framework

Entendido os conceitos de Inversão de Controle e Injeção de Dependência, as características e formas de uso do Spring tornam-se mais simples. A Figura 2 apresenta a estrutura do Spring.

Text Box:    Figura 2 - Estrutura do Spring.

O módulo Spring Core representa as principais funcionalidades do Spring, no qual o principal elemento é o BeanFactory. Trata-se de uma implementação do padrão Factory, responsável em remover a programação de Singletons e permitindo o baixo acoplamento entre a configuração e a especificação de dependências, de sua lógica de programação.

O módulo Spring DAO provê uma camada de abstração para JDBC, eliminando grande parte da codificação necessária para interagir com um banco de dados. O módulo ORM, entretanto, provê integração do Spring com outros frameworks para persistência de objetos, como Hibernate e iBatis. Para prover uma implementação de Orientação a Aspectos que permite a definição de pointcuts e methods interceptors, existe o módulo Spring AOP.

Para prover funcionalidades específicas para projetos Web, tem-se o módulo Spring Web. São funcionalidades como componentes para upload de arquivos e suporte para utilização de Inversão de Controle neste tipo de aplicação. O módulo Spring MVC, entretanto, fornece uma implementação de framework Web, similar ao Struts.

Container de Inversão de Controle (IoC)

Inicialmente, é preciso entender o conceito, adotado pelo Spring, de beans. Para este framework, qualquer objeto que forma sua aplicação e que está sob controle do Spring, é considerado um bean. Enfim, um bean trata-se apenas de um objeto de sua aplicação e nada mais. O Container IoC é o responsável pelo gerenciamento destes beans.

Estes beans, entretanto, muitíssimo provavelmente possuem dependências entre si. Estas dependências são definidas através de metadados. O Container IoC obtém essas configurações e, partindo destas configurações, gerencia a dependência entre os beans. Neste contexto, a interface org.springframework.beans.factory.BeanFactory representa o Container IoC do Spring. Conforme explicitado anteriormente, uma implementação desta interface é responsável em realizar o trabalho do Assembler, apresentado na Figura 01. Existem diversas implementações de BeanFactory, sendo a XmlBeanFactory a implementação mais comum. Nesta, toda configuração de dependência entre os objetos é definida em um arquivo XML.

Aplicação de Exemplo

Para este exemplo será utilizada a versão 1.5 da JDK da Sun e a IDE Eclipse, em sua versão 3.2. A versão do Spring a ser adotada será a 2.0 Release Candidate 2. Trata-se de uma aplicação simples, na qual será possível cadastrar e listar clientes de uma empresa fictícia. Inicialmente, não adotaremos nenhuma interface web. Pretendemos, entretanto, apresentar uma solução mais completa, adotando outros frameworks, como o Struts, nos próximos artigos.

Text Box:    Figura 3 - Diagrama de Classes.

O diagrama de classes da Figura 03 apresenta as interfaces e classes que compõem esta aplicação. As interfaces ClienteDao e Sistema delimitam o comportamento de objetos que implementam a persistência de objetos Cliente e o acesso ao sistema, respectivamente. Neste contexto, temos ainda a classe ClienteHibernateDao, que implementa a interface ClienteDao, permitindo a persistência de objetos do tipo Cliente através do framework Hibernate (www.hibernate.org), e SistemaImpl que fornece uma implementação concreta para a interface Sistema.

Text Box:    Figura 4 - Estrutura de Pacotes.

O projeto estará conforme a estrutura de pacotes ilustrada na Figura 4. O pacote padrão da aplicação é br.com.imasters.spring. O pacote br.com.imasters.spring.beans contém as classes de negócio, neste caso, a classe Cliente. Finalizando o pacote br.com.imasters.spring.dao e br.com.imasters.spring.dao.hibernate sustentam as classes responsáveis pela persistência do sistema.

Clique na imagem para ampliar.


O Spring pode ser obtido no endereço http://www.springframework.org/, no link Downloads. A Figura 05 apresenta o projeto no Eclipse configurado com as classes do Spring. As outras bibliotecas que acompanham o pacote de distribuição do Spring são necessárias apenas caso algumas funcionalidades extras sejam utilizadas. Entretanto, observar a necessidade de ter as bibliotecas commons-logging.jar e log4j.jar no classpath.

public class Cliente {
private int id ;
private String nome ;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}

}

Listagem 03

A Listagem 03 apresenta a classe Cliente, contendo apenas dois campos: id e nome. A Listagem 04 demonstra os métodos existentes na interface ClienteDao(Listagem 4), e que são implementados na classe ClienteDaoHibernate. Vamos manter o conteúdo desta classe simples, pois o objetivo deste artigo não é apresentar o Hibernate, e sim o funcionamento do Spring. Portanto, a listagem 08 apresenta esta classe apenas enviando mensagens ao console, através da chamada a System.out.println.

public interface ClienteDao {
Collection getClientes() ;
void incluirCliente ( Cliente cliente ) ;
}

Listagem 04

A interface Sistema, e sua implementação SistemaImpl são apresentadas nas Listagens 5 e 6, respectivamente. Pode-se observar, neste exemplo, que a classe SistemaImpl apenas repassa as chamadas de seus métodos para métodos semelhantes da interface ClienteDao.

public interface Sistema {

Collection getClientes() ;
void incluirCliente ( Cliente cliente ) ;

}

Listagem 05

É possível observar que a classe SistemaImpl funciona conforme o padrão Facade, fornecendo um único caminho de entrada para o sistema, evitando que o usuário perca-se na complexidade inerente de todo sistema.

public class SistemaImpl implements Sistema {
private ClienteDao daoCliente ;

public Collection getClientes() {
return this.daoCliente.getClientes() ;
}

public void incluirCliente(Cliente cliente) {
this.daoCliente.incluirCliente(cliente);
}

public ClienteDao getDaoCliente() {
return daoCliente;
}

public void setDaoCliente(ClienteDao daoCliente) {
this.daoCliente = daoCliente;
}

}

Listagem 06

Falta, ainda, definir a configuração de dependência entre os objetos desta aplicação. Para isto, criamos um arquivo chamado applicationContext.xml, e o colocamos no classpath da aplicação. A Listagem 07 apresenta o conteúdo deste arquivo. Neste arquivo, definimos o bean ClienteDao, como sendo do tipo br.com.imasters.spring.dao.hibernate.ClienteHibernateDao.







Listagem 07

Definimos também o bean Sistema, do tipo br.com.imasters.spring.SistemaImpl e que possui uma propriedade clienteDao que deve ser injetada com um objeto definido no bean ClienteDao.

public class ClienteHibernateDao implements ClienteDao {

public Collection getClientes() {
System.out.println ( "Listagem" ) ;
Collection list = new ArrayList() ;
Cliente c = new Cliente() ;
Cliente c2 = new Cliente() ;
Cliente c3 = new Cliente() ;
list.add(c);
list.add(c2);
list.add(c3);
return list;
}

public void incluirCliente(Cliente cliente) {
System.out.println ( "Cliente Incluído" ) ;
}

}

Listagem 08

Finalmente, precisamos ativar o container IoC, de forma que as dependências entre os objetos possam ser resolvidas. A Listagem 09 encarrega-se de apresentar a classe Aplicacao.java, responsável por invocar o framework.

public class Aplicacao {

public static void main ( String[] args ) {
XmlBeanFactory factory = new XmlBeanFactory ( new FileSystemResource ( "applicationContext.xml" ) ) ;
Sistema sistema = (Sistema) factory.getBean ( "Sistema" ) ;
sistema.incluirCliente ( new Cliente() ) ;
}

}

Listagem 09

Conclusão

O principal objetivo deste artigo foi apresentar o framework Spring, o qual acredito ter sido cumprido. Muitos questionam a real necessidade de utilizar o Spring, já que padrões como Abstract Factory fornecem um nível de abstração próximo ao que o Spring fornece. Entretanto, o Spring permite que sua aplicação seja extremamente “plugável”, em runtime. A inserção de uma nova biblioteca e a alteração do arquivo applicationContext.xml é o suficiente para alterar uma estratégia de persistência, por exemplo. Com padrões como AbstractFactory, para conquistar este mesmo feito é necessário, pelo menos, realizar a recompilação de todo o código fonte.

fonte:http://imasters.uol.com.br/artigo/4497/spring_framework_introducao

Nenhum comentário: