7 de jan de 2013

Spring 3 - Injeção de dependências em objetos fora do contexto do Spring.

Injeção de dependências é uma abordagem em POO que tenta facilitar a construção de objetos. Nesta abordagem um gerenciador deve conhecer os objetos da aplicação e as dependências entre eles. O gerenciador se encarrega de ligar os objetos que dependem entre si (injeta as dependências). Vamos ver Injeção de dependências com Spring Framework 3.



Considere as duas classes abaixo para este exemplo:

public class UsuarioDAO {

   public void insereUsuario(String nome) {
        System.out.println("Usuario " + nome + " inserido com Sucesso !");
   } 

}
public class UsuarioBO {

    private UsuarioDAO usuarioDAO;

    public void insereUsuario(String nome) {
      usuarioDAO.insereUsuario(nome);
    }

    public void setUsuarioDAO(UsuarioDAO usuarioDAO) {
        this.usuarioDAO = usuarioDAO;
    }

}
Note que a classe UsuarioBO depende da classe UsuarioDAO.

O spring possui um arquivo de configuração applicationContext.xml onde são definidos os objetos controlados pelo spring:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-autowire="byName">

<bean id="usuarioDAO" class="test.dao.UsuarioDAO" scope="singleton" />

<bean id="usuarioBO" class="test.bo.UsuarioBO" scope="singleton" />

</beans>

No arquivo acima foram declarados dois beans, usuarioDAO e usuarioBO dos tipos UsuarioDAO e UsuarioBO respectivamente. O Spring ao ser carregado cria os dois objetos e injeta automaticamente o usuarioDAO no usuarioBO. Ele descobre que o bean usuarioBO depende do usuarioDAO devido a opção default-autowire="byName" que procura por beans definidos com o id igual ao nome de uma propriedade. Também podemos definir explicitamente as dependências entre os objeto:

<bean id="usuarioDAO" class="test.dao.UsuarioDAO" scope="singleton" />

<bean id="usuarioBO" class="test.bo.UsuarioBO" scope="singleton" >
    <property name="usuarioDAO" ref="usuarioDAO"/>
</bean>

Esta é a maneira convencional de se definir os objetos que serão controlados pelo Spring.

Controlando objetos fora do contexto do Spring


A novidade aqui é a anotação @Configurable, que permite ao Spring injetar dependências mesmo em objetos que estejam fora de seu controle. Isso é possível graças a programação orientada a aspecto que insere pointcuts nos objetos com essa anotação para settar as propriedades com o spring. Você pode usar um comando new SuaClasse() e na linha debaixo usar o objeto sem se preocupar com as dependencias. A utilidade que eu vi nessa opção é que muitas vezes se usa mais de um framework que tem o mesmo propósito, neste caso criação de objetos. Então se você desejar configurar um objeto criado com outro framework é possivel com essa anotação. Um exemplo de dois frameworks que podem ser usados juntos é Spring + JSF em que é possível injetar objetos do Spring diretamente nos ManagedBeans criados pelo JSF e assim usar os benefícios de ambos.

A seguir vou apresentar um exemplo de como deve ficar os arquivos de configuração e uma classe que usa a anotação @Configurable de uma aplicação web.

Para carregar o contexto do spring em uma aplicação web basta adicionar as seguintes linhas no arquivo web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext*.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

A seguir crie o arquivo applicationContext.xml. Certifique-se que este esteja no classpath da sua aplicação.

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    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
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    default-autowire="byName">
    
    <context:load-time-weaver aspectj-weaving="on"/>
    <context:spring-configured />    
    <context:annotation-config />
    <context:component-scan base-package="test.servlets" />

    <bean id="usuarioDAO" class="br.ufu.prograd.siprot.dao.UsuarioDAO" scope="singleton" />
    <bean id="usuarioBO" class="br.ufu.prograd.siprot.bo.UsuarioBO" scope="singleton" />

</beans>

Veja que é necessário definir o pacote onde o Spring irá procurar por definições com anotações. No nosso caso foi <context:component-scan base-package="test.servlets" />


Vamos criar nosso servlet para testar a anotação @Configurable:

package test.servlets;

@Configurable(autowire=Autowire.BY_NAME)
public class UsuarioServlet extends HttpServlet {
    
    private UsuarioBO usuarioBO;
    
    private void setUsuarioBO(UsuarioBO usuarioBO) {
        this.usuarioBO = usuarioBO;
    }
    
    public void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String nome = request.getParameter("nome");
        usuarioBO.insereUsuario(nome);
        request.getRequestDispatcher("sucesso.jsp").forward(request, response);
    }
    
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }
    
    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }
 
}

Mapeando o Servlet no web.xml:

    <servlet>
        <servlet-name>UsuarioServlet</servlet-name>
        <servlet-class>test.servlets.UsuarioServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>UsuarioServlet</servlet-name>
        <url-pattern>/UsuarioServlet/*</url-pattern>
    </servlet-mapping>

Veja que não foi preciso que o servlet soubesse onde deveria buscar o objeto usuarioBO para realizar operações na entidade usuário por que esse objeto foi criado e configurado pelo spring e injetado no servlet quando ele foi criado.


Você precisará dos seguintes jars no classpath da sua aplicação para que o exemplo funcione:

spring-core-3.1.2.RELEASE.jar
spring-expression-3.1.2.RELEASE.jar
spring-beans-3.1.2.RELEASE.jar
spring-aop-3.1.2.RELEASE.jar
spring-context-3.1.2.RELEASE.jar
spring-context-support-3.1.2.RELEASE.jar
spring-tx-3.1.2.RELEASE.jar
spring-jdbc-3.1.2.RELEASE.jar
spring-web-3.1.2.RELEASE.jar
spring-aspects-3.1.2.RELEASE.jar
aspectjrt-1.6.12.jar
aspectjweaver-1.6.12.jar

Caso esteja usando o tomcat você precisará incluir na pasta $CATALINA_HOME/lib o arquivo spring-tomcat-weaver-2.5.6.jar. Além disso você deve criar o arquivo context.xml na pasta META-INF da sua aplicação:

<?xml version='1.0' encoding='utf-8'?>
<Context>
    <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/>
</Context>

Se você quiser usar o maven para baixar os jars o pom.xml fica assim:

    <!-- Shared version number properties -->
    <properties>
        <org.springframework.version>3.1.2.RELEASE</org.springframework.version>
    </properties>

    <dependencies>
        <!-- Spring Framework 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-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>

        <!-- Tomcat weaver -->

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tomcat-weaver</artifactId>
            <version>2.5.6</version>
            <exclusions>
                <exclusion>
                    <groupId>tomcat</groupId>
                    <artifactId>catalina</artifactId>
                </exclusion>
            </exclusions>
            <scope>provided</scope>
        </dependency>

        <!-- AspectJ -->

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.12</version>
        </dependency>
    </dependencies>

Lembrando que você deve colocar a biblioteca spring-tomcat-weaver-2.5.6.jar na pasta  $CATALINA_HOME/lib e criar o arquivo context.xml com ClassLoader do Spring.

Nenhum comentário:

Postar um comentário