É comum vermos hoje em dia o uso do Spring para realizar injeção de dependências no lado do servidor, evitando que o desenvolvedor tenha que preocupar-se com a instanciação e controle das instâncias das classes que estão sendo gerenciadas.
O problema começa a aparecer quando além dessa aplicação WEB temos uma aplicação stand-alone que precisa usar os mesmos recursos gerenciáveis do Spring, em outras palavras, imagine que temos um formulário que precisa usar recursos que o Spring está gerenciando, os beans. Nesse caso precisaremos fazer uso de uma classe chamada ClassPathXmlApplicationContext para capturar a instância do Bean. Nosso artigo terá como principal foco mostrar como realizar a captura de beans do Spring através desta classe.
Projeto com Spring
É importante entender que uma aplicação stand-alone não precisa que um container como o TomCat ou um servidor de aplicação como o glassfish esteja em execução para funcionar, por isso chama-se “stand-alone”, pelo fato de conseguir manter-se ativa por si própria.
Aqui torna-se muito claro porque trabalhar com MVC (Model-View-Controller): Se você desenvolver a sua camada Model e Controller ao ponto que estes não saibam qual será o View (Mobile, Web, Standalone …) que os usará então você terá um sistema com baixo acoplamento e alta reusabilidade. Imagine que hoje nosso sistema possui uma View toda em WEB (com páginas HTML), amanhã poderemos criar mais uma View Stand-alone (com Formulários em JSE) para aqueles usuários que desejarem usar o sistema de forma privada sem nenhuma conexão externa. Você tem exatamente a mesma lógica sendo executada para duas Views distintas: essa é uma boa prática para trabalhar-se pois qualquer mudança realizada nessas camadas de lógica refletem diretamente em todas as Views ligadas a elas.
Vamos criar um projeto simples apenas para entender o que foi explicado anteriormente, uma View WEB e uma View Stand-alone conectadas ao mesmo modelo de negócio, mas em contextos diferentes. Primeiramente vamos configurar um projeto que usará o Spring e injetar uma classe.
Criamos um projeto usando o Maven, assim poderemos usar o gerenciamento de bibliotecas através do arquivo pom.xml. Nosso projeto possui a mesma estrutura apresentada na Figura 1.
Vamos entender a estrutura de pastas do nosso projeto:
- src/main/java: Contém todo o código Java, tais como Beans, Helpers, Utils e etc.;
- src/main/resourcers: Contém o arquivo de configuração do Spring (em nosso caso, mas pode possuir outros recursos que serão utilizados pelo projeto);
- src/main/webapp: Local onde ficará o conteúdo web acessível pelo usuário;
- src/main/webapp/WEB-INF: Local que não pode ser acessado pelo usuário mesmo sendo dentro do webapp, esta é uma exceção para que possam ser colocados arquivos de configuração, como é o caso do web.xml e as bibliotecas do projeto.
Usamos o pom.xml para configurar as bibliotecas do spring e todas as suas dependências, como mostra o código da Listagem 1.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>br.com.springlab</groupId>
<artifactId>springlab</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Springlab</name>
<url>http://maven.apache.org</url>
<properties>
<org.springframework.version>3.0.6.RELEASE
</org.springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
</dependencies>
<build>
<finalName>springlab</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.5</version>
<configuration>
<targetJdk>1.6</targetJdk>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
Configurado o pom.xml temos agora as bibliotecas necessárias para fazer uso dos recursos do Spring Framework, principalmente a injeção de dependências. O arquivo applicationContext.xml possui as configurações necessárias para o Spring saber o que deverá ser gerenciado por ele, como mostra a Listagem 2.
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- Seta anotaçoes para serem usadas pelo Spring -->
<context:annotation-config />
<!-- Define o pacote onde o Spring vai procurar por beans anotados -->
<context:component-scan base-package="br.com.springlab" />
<!-- define que as transaçoes irao ser anotadas -->
<tx:annotation-driven proxy-target-class="true" />
</beans>
Nesse arquivo definimos que o Spring deverá ler as anotações realizadas no projeto, tais como @Service, @Component e etc. Logo em seguida definimos o pacote que o spring deverá ler, no caso apenas o br.com.springlab.
Pronto, nosso projeto está configurado com Spring Framework e pronto para realizar as injeções de dependências necessárias. Criaremos dois pacotes: o br.com.springlab.logic e o br.com.springlab.standalone, onde o primeiro terá a lógica que desejamos executar e o segundo apenas fará a chamada a ele.
Vejamos a classe MyLogic, presente na Listagem 3.
package br.com.springlab.logic;
import org.springframework.stereotype.Service;
@Service(value = "myLogic")
public class MyLogic {
public void showPI(){
System.out.println("Iniciando display PI");
for(int i = 1; i <= 10; i++){
System.out.println(i * Math.PI);
}
System.out.println("Finalizando display PI");
}
}
A nossa classe MyLogic tem a anotação @Service(value = “myLogic”), isso significa que em qualquer ponto gerenciável pelo spring (apenas dentro do escopo br.com.springlab) podemos fazer chamada ao bean “myLogic” que ele deverá retornar à instância atual da classe MyLogic. O Spring trabalha com o padrão de projeto Singleton por padrão ao gerenciar essas classes, sendo a instância retornada sempre será igual enquanto a aplicação não for reiniciada, seja por um container web (Tomcat, por exemplo) ou por uma aplicação stand-alone.
A nossa lógica é bem simples, contém um método chamado showPI() que mostra o valor de PI multiplicado pelo valor de I, que é incrementado de 1 até 10, ou seja: PI * 1, PI * 2, PI * 3 e assim por diante. Essa lógica foi colocada apenas para mostrarmos o uso do Spring Context, mas você poderia usar qualquer outra lógica ou até mesmo um simples “System.out.println(“hello world from Spring Context”);” que a ideia seria a mesma.
Podemos usar essa classe gerenciável pelo Spring através de uma aplicação stand-alone ou uma página web, como JSF por exemplo. Em nosso caso vamos demonstrar como funciona o uso através do stand-alone, como mostra a Listagem 4.
package br.com.springlab.standalone;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import br.com.springlab.logic.MyLogic;
public class MainApp {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyLogic myLogic = (MyLogic) context.getBean("myLogic");
myLogic.showPI();
}
}
Stand-alone trata-se de um conceito para aplicações que podem ser executadas sem ferramentas auxiliares. O Java por si só não pode ser considerado stand-alone por precisar de uma JVM para ser executado, diferente do C, que após compilado não precisa de ferramentas auxiliares. Mas em nosso contexto podemos considerar como uma aplicação stand-alone pois trata-se da execução das dependências gerenciáveis pelo Spring sem a necessidade de um servidor de aplicação ou container web.
A classe MainApp é bem simples, mas eficaz no que precisamos realizar, que no caso é o retorno do bean gerenciável MyLogic. Vejamos em detalhes:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Criamos um objeto do tipo ClassPathXmlApplicationContext passando como parâmetro o caminho do arquivo de configuração “applicationContext.xml”, neste caso o arquivo deve estar na pasta src/main/resources, assim como explicamos logo no início. Ao passo por essa linha o console já demonstra algo sendo realizado, parecido com a Listagem 5.
Jul 09, 2015 8:15:07 PM org.springframework.context.support.AbstractApplicationContext
prepareRefresh
Informações: Refreshing
org.springframework.context.support.ClassPathXmlApplicationContext@556292a4: startup date
[Thu Jul 09 20:15:07 BRT 2015]; root of context hierarchy
Jul 09, 2015 8:15:07 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader
loadBeanDefinitions
Informações: Loading XML bean definitions from class path resource [applicationContext.xml]
Jul 09, 2015 8:15:08 PM org.springframework.beans.factory.support.DefaultListableBeanFactory
preInstantiateSingletons
Informações: Pre-instantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@646282c1: defining beans
Informações: Pre-instantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@646282c1: defining beans
Informações: Pre-instantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@646282c1: defining beans
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org
.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework
.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation
.internalCommonAnnotationProcessor,myLogic,org.springframework.aop.config
.internalAutoProxyCreator,org.springframework.transaction.annotation
.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor
.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor];
root of factory hierarchy
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org
.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework
.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation
.internalCommonAnnotationProcessor,myLogic,org.springframework.aop.config
.internalAutoProxyCreator,org.springframework.transaction.annotation
.AnnotationTransactionAttributeSource#0,org.springframework.transaction
.interceptor.TransactionInterceptor#0,org.springframework.transaction.config
.internalTransactionAdvisor]; root of factory hierarchy
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor,
org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org[org.
springframework.context.annotation.internalConfigurationAnnotationProcessor,org.
springframework.context.annotation.internalAutowiredAnnotationProcessor,org.
springframework.context.annotation.internalRequiredAnnotationProcessor,org.
springframework.context.annotation.internalCommonAnnotationProcessor,myLogic,
org.springframework.aop.config.internalAutoProxyCreator,org.springframework.
transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.
transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.
config.internalTransactionAdvisor]; root of factory hierarchy.springframework.
context.annotation.internalRequiredAnnotationProcessor,org.springframework.
context.annotation.internalCommonAnnotationProcessor,myLogic,org.springframework.
aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.
AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.
TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor];
root of factory hierarchy
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor,
org.springframework.context.annotation.internalAutowiredAnnotationProcessor,
org.springframework.context.annotation.internalRequiredAnnotationProcessor,
org.springframework.context.annotation.internalCommonAnnotationProcessor,myLogic,
org.springframework.aop.config.internalAutoProxyCreator,org.springframework.
transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.
transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.
config.internalTransactionAdvisor]; root of factory hierarchy
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor,
org.springframework.context.annotation.internalAutowiredAnnotationProcessor,
org.springframework.context.annotation.internalRequiredAnnotationProcessor,
org.springframework.context.annotation.internalCommonAnnotationProcessor,myLogic,
org.springframework.aop.config.internalAutoProxyCreator,org.springframework.
transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.
transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.
config.internalTransactionAdvisor]; root of factory hierarchy
Lendo de forma resumida, no console percebemos que o Spring está fazendo a leitura do arquivo XML e preparando os beans para serem utilizados.
O Spring reconhece o pacote que está sendo gerenciado e permite que você capture os beans que desejar, em nosso caso usamos o seguinte:
MyLogic myLogic = (MyLogic) context.getBean("myLogic");
O getBean() retorna a instância do “myLogic” e agora podemos fazer a chamada ao método necessário:
myLogic.showPI();
E nosso retorno será:
Iniciando display PI
3.141592653589793
6.283185307179586
9.42477796076938
12.566370614359172
15.707963267948966
18.84955592153876
21.991148575128552
25.132741228718345
28.274333882308138
31.41592653589793
Finalizando display PI
Vimos neste artigo que a reusabilidade de código é um ponto importante a ser notado quando trabalhamos com qualquer tipo de projeto, principalmente com mais de uma View (mobile, web, desktop e etc.).
Neste caso o ideal é criar uma classe responsável apenas por fazer a chama aos beans, não precisando criar uma instância de ClassPathXmlApplicationContext a cada chamada, sobrecarregando muito o sistema.