escrever sem o recurso.
Entretanto, é importante salientar algumas limitações. A principal vantagem do formato XML é o fato deste possibilitar alterar o funcionamento do nosso sistema sem a necessidade de recompilarmos o código fonte. Em situações em que a custo- mização seja um aspecto crítico, como por exemplo no desenvolvimento de produtos de prateleira, a adoção das anotações podem acarretar mais perdas que ganhos. Ape- sar disto, é importante mencionar que qualquer configuração feita usando anotações pode ser sobrescrita em um arquivo XML. Por exemplo, é possível declarar um bean específico em XML e este será levado em consideração ao invés do mesmo definido usando anotações.
Outro ponto importante a ser levantado: a redução da configuração muitas vezes é ilusória quando usamos anotações, pois na prática na maior parte das vezes o que realmente estamos fazendo é transferir informações do formato XML para dentro de nossas classes.
4.4
Configuração programática com Java
A versão 3.0 do Spring trouxe como novidade a possibilidade de configurarmos nosso container com código Java, oferecendo possibilidades interessantes ao desen- volvedor como por exemplo configuração programática e, finalmente, a eliminação total da configuração XML.
Os modos de configuração até então apresentados são declarativos. O desen- volvedor descreve ao container o que instanciar e este se encarrega do trabalho de injeção de dependências e inicialização dos beans. Com a configuração baseada em código Java o programador passa a agir imperativamente sobre o ciclo de vida dos beans. Com base no que aprendemos até este momento, passo para o leitor um desa- fio: usando XML ou anotações, como definir qual tipo de classe instanciar baseado em algum fator de execução do sistema como por exemplo a hora corrente? Como veremos, este tipo de problema é trivialmente resolvido usando configuração base- ada em código.
@Configuration e @Bean
Nos formatos de configuração apresentados anteriormente o ponto de par- tida sempre era um arquivo no formato XML. Com a configuração programática nosso ponto de partida passa a ser uma ou mais classes que possuam a anotação
@Configurationque, por baixo dos panos, consiste em mais um estereótipo (stere- otype) oferecido pelo Spring.
Toda classe anotada com@Configurationé entendida pelo container como um bean responsável por armazenar uma ou mais definições de beans. Dado que esta- mos trabalhando com um estereótipo, seu uso é exatamente igual ao que já vimos na seção anterior como pode ser visto no código abaixo:
import org.springframework.context.annotation.Configuration; // Sem definir um nome explicitamente
@Configuration
public class JavaConfig {...} // Definindo um nome explicitamente
@Configuration("configuracaoJava")
public class JavaConfig {...}
Se@Configurationdefine um repositório de beans, em seu interior devemos incluir a anotação@Bean, que deve ser inserida apenas em funções equivale à tag
<bean>que usamos na configuração no formato XML. No código abaixo podemos ver alguns exemplos de seu uso:
@Configuration
public class JavaConfig {
@Bean
public DAOUsuario daoUsuario() {
return new DAOUsuario(); }
// Definindo mais de um nome para o mesmo bean
@Bean(name={"daoProduto","produtoDAO"})
public DAOProduto criarDAOProduto() {
return new DAOProduto(); }
// Definindo o nome do bean explicitamente
@Bean(name="datasource")
public DataSource getDataSource() { DataSource ds = null;
GregorianCalendar data = new GregorianCalendar();
if (data.get(GregorianCalendar.HOUR_OF_DAY) < 13) { ds = new DataSourceUsuarios();
4.4. Configuração programática com Java Casa do Código } else { ds = new DataSource(); } return ds; } }
Vemos no exemplo anterior três usos da anotação@Bean. No primeiro caso, apli- camos a anotação sobre um método sem definir explicitamente o nome do bean. Nesta situação o nome do bean corresponderá ao nome do método que recebeu a anotação. Assim como na configuração XML, também é possível definir múltiplos nomes para um bean (aliases), que é o que fazemos no segundo caso da anotação, passando como parâmetro um array de strings contendo estes nomes.
Como pode ser observado, tudo o que os dois primeiros métodos do nosso có- digo anterior simplesmente criam uma nova instância do bean. O uso realmente interessante pode ser observado no terceiro caso em que usamos a hora corrente do sistema para escolher qual implementação deve ser instanciada.
Finalmente, dado que@Configurationna realidade é um estereótipo, isto é, uma variação de@Component, poderíamos informar o container via XML a usar configu- rações baseadas em anotações com um arquivo similar ao exposto abaixo, sabendo que no pacotebr.com.casadocodigo.javahá apenas a classeJavaConfig: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="br.com.casadocodigo.java"/> </beans>
Ciclo de vida e escopo
A anotação@Beanpossui dois atributos opcionais que nos permitem tirar pro- veito do ciclo de vida do bean por ela representado. Estes atributos sãoinitMethod edestroyMethod, que recebem como valor o nome do método a ser executado após
a instanciação e injeção de dependências no bean e antes de sua destruição pelo con- tainer. No código abaixo podemos ver um exemplo de seu uso:
@Bean(initMethod="iniciar", destroyMethod="destruir")
public DAOUsuario getDaoUsuario() {
return new DAOUsuario(); }
Também podemos definir o escopo de um bean. Neste caso, adicionamos a ano- tação@Scopeprovida pelo Spring. No caso, seu uso é exatamente o mesmo que vimos na configuração baseada por anotações, a diferença é que iremos anotar o método responsável pela instanciação do bean.
@Scope("prototype")
@Bean(initMethod="iniciar", destroyMethod="destruir")
public DAOUsuario getDaoUsuario() {
return new DAOUsuario(); }
@Scope("request")
@Bean
public DAOProduto daoProduto() {
return new DAOProduto(); }
Injetando dependências
Nos exemplos expostos até este momento todos os métodos construtores de be- ans expostos simplesmente instanciam objetos sem se preocupar com a injeção de dependências. Isto porque na realidade estamos novamente aqui usando a configu- ração baseada em anotações que, na prática, é o autowiring baseado em tipo.
Na configuração baseada em Java, no entanto, é importante observar que, uma vez que o método@Beanretorne seu valor, o container se encarrega de injetar todas as dependências deste objeto, o que pode gerar algumas situações para os iniciantes, como por exemplo a situação exposta no código abaixo:
@Configuration
public class JavaBuilder {
@Bean
public DataSource dataSource() {
4.4. Configuração programática com Java Casa do Código }
@Bean
public DAOProduto daoProduto() {
return new DAOProduto(); }
@Bean
public DAOProduto preConfigurado() { DAOProduto dao = new DAOProduto(); dao.setDataSource(new DataSource());
return dao; }
}
A primeira vista podemos ter a impressão de que o beanpreConfiguradoex- posto acima recebe como dependência uma instância diferente da definida pelo bean
dataSource, certo? Errado: uma vez obtida a instância do bean o container irá in- jetar como dependência o bean retornado pelo métododataSource(). A única ma- neira de se evitar este comportamento consiste em não implementar na classe DAO- Produto um método setter para o atributodataSource, injetando esta dependência apenas pelo construtor.
Dado que uma classe anotada com@Configurationé em si um bean, esta tam- bém pode receber dependências. Sendo assim, poderíamos injetar uma dependência nesta e em seguida reaproveitá-la nas definições de beans que esta contenha, tal como no exemplo abaixo:
@Configuration
public class JavaBuilderDependencia {
@Autowired
private DataSource dataSource;
@Bean
public DAOProduto daoProduto() {
DAOProduto daoProduto = new DAOProduto(); daoProduto.setDataSource(dataSource);
return daoProduto; }
Excluindo a configuração XML
Finalmente, como mencionado no início desta seção, veremos como tornar o Spring 100% livre da configuração XML. Para tal iremos usar uma implemen- tação doApplicationContext chamadaAnnotationConfigApplicationContext, presente no pacote org.springframework.context.annotation. Ao contrário deClassPathXmlApplicationContexteFileSystemXmlApplicationContext, esta classe recebe como parâmetro uma ou mais classes que contenham definições de beans, ou seja, aquelas que possuem uma anotação que as identifiquem como imple- mentadoras de algum estereótipo reconhecido pelo Spring.
Há apenas duas novidades neste tipo de container, seus construtores e o método
register. É possível instanciar este contexto de aplicação de qualquer uma das for- mas expostas no código abaixo:
import org.springframework.context.annotation. AnnotationConfigApplicationContext;
...
AnnotationConfigApplicationContext context; // Pelo construtor padrão
context = new AnnotationConfigApplicationContext();
/*
Passando apenas uma classe contendo definições de beans @Configuration ou alguma classe que possua um estereótipo anotado
*/
context = new AnnotationConfigApplicationContext(JavaBuilder.class); // Passando uma lista de classes anotadas
context = new AnnotationConfigApplicationContext( JavaBuilder.class, DataSource.class);
// Passando o nome de um pacote base aonde buscar definições
context = new AnnotationConfigApplicationContext("br.com.casadocodigo"); // Passando o nome de mais de um pacote aonde buscar definições
context = new AnnotationConfigApplicationContext(
"br.com.casadocodigo.java", "br.com.casadocodigo.daos");
4.4. Configuração programática com Java Casa do Código com XML em que podemos usar filtros para definir o que incluir e o que não incluir em nosso container, não possuímos este recurso usando apenas os construtores for- necidos por esta classe. Para que possamos possuir um controle mais fino sobre quais definições incluir usamos o métodoregister, que recebe como parâmetro uma lista de classes anotadas a serem parseadas pelo container. É um método bas- tante útil quando queremos aplicar alguma lógica em cima de quais beans queremos em nosso container com base em condições externas à nossa aplicação.
Um bom uso deste método pode ser visto no seguinte exemplo:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); if (condição) { context.register(DataSource.class); } else { context.register(DataSourceCustomizado.class); } context.register(DAOProduto.class, DAOUsuario.class);
// Atualiza a configuração do container após este ter sido alimentado // com as classes acima
context.refresh();
Após termos invocado quantas vezes forem necessárias o métodoregisteré importante que seja executado o métodorefreshdo container, que irá atualizar suas definições internas de beans.
Para finalizar, outro método interessante deste contexto de aplicação éscanque recebe como parâmetro uma lista de pacotes base aonde serão buscadas classes ano- tadas a serem incluídas em nosso container. Um exemplo do seu uso pode ser visto no código abaixo: AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); if (condição) { context.scan("br.com.casadocodigo"); } else {
context.scan("br.com.casadocodigo2", "br.com.dao"); }
// Atualizamos o estado interno do container context.refresh();
Vantagens e limitações da configuração Java
A grande vantagem da configuração baseada em código fonte é a possibilidade do desenvolvedor possuir maior controle sobre o modo como seus beans são instan- ciados e seus estados internos definidos. Para os que não gostam do formato XML, pode-se dizer que outra grande vantagem é a possibilidade de nos livrarmos com- pletamente deste formato.
É importante observar no entanto que as configurações baseadas em Java não fo- ram feitas para substituir completamente o formato XML, mas sim para enriquecê- lo, e, como toda ferramenta, deve ser usado sem excessos pois possui a grande des- vantagem de sempre exigir recompilação de código fonte quando alguma configu- ração precisa ser alterada, o que torna esta opção mais difícil de ser adotada em ambientes nos quais customização seja uma prioridade alta.