Spring IoC Container: Utilizando beans fuera del contenedor (I)

logo spring

 Es posible que en alguna ocasión en una aplicación web en la que se emplee el contenedor de Spring sea necesario utilizar algunos de sus beans en clases instanciadas fuera del contenedor por lo que no podemos recurrir a la inyección de dependencias. Por ejemplo, puede darse el caso de que tengamos que integrar en nuestra aplicación un viejo servicio web de Axis que termine invocando los nuevos beans de negocio definidos en Spring, o bien tengamos que reutilizar algún servelt.

En estas situaciones, no nos quedará más remedio que buscar una estrategia que permita acceder a los beans de Spring que necesitemos. En el presente artículo se va a exponer de forma simple y práctica algunas posibles soluciones para conseguir resolver esta problemática.

Entorno de pruebas:

Requisitos: Conocimientos básicos de Spring IoC y servlets.

Proyecto para pruebas

Para probar las distintas estrategias se va a utilizar un proyecto web que constará únicamente de un servlet y del contenedor de Spring con un bean de ejemplo. Este proyecto se gestionará con Maven, por lo que en primer lugar hay que crear la estructura corrrespondiente. En nuestro caso, sólo hacen falta los directorios “/src/main/java” y “/src/main/webapp”.

En el pom.xml hay que definir dos dependencias: una para poder utilizar Spring IoC en una aplicación web (en este caso concreto, y para la serie 3.x, será spring-web), y otra para los servlets. Ya que esta última está incluída en el contenedor J2EE donde se desplegará el war (en mi caso será Tomcat 6) , se puede definir como provided.

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.danielme.blog.springioc</groupId>
	<artifactId>obtenerbeans</artifactId>
	<version>1.0</version>
	<name>obtenerbeans</name>
	<packaging>war</packaging>
	<description>Pruebas de uso de beans gestionados por Spring fuera del contenedor. Proyecto base.</description>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<licenses>
		<license>
			<name>El presente proyecto Maven es el código de ejemplo utilizado en el artículo "Spring IoC Container: Utilizando beans fuera del contenedor", 
			publicado con licencia "Creative Commons Reconocimiento-NoComercial-CompartirIgual 3.0  Unported" en la web "http://danielme.com"</name>
		</license>
	</licenses>

	<build>
		<pluginManagement>
			<plugins>

				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>2.3.2</version>
					<configuration>
						<source>1.5</source>
						<target>1.5</target>
					</configuration>
				</plugin>

			</plugins>
		</pluginManagement>
	</build>
	

	<dependencies>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>3.1.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>

	</dependencies>

</project>

El servlet se limitará a devolver una página en html escribiéndola en el response:

package com.danielme.blog.springioc.obtenerbeans;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet en el se recuperará el bean de Spring.
 * @author http://danielme.com
 *
 */
public class ServletPrueba extends HttpServlet 
{	
	/**
	 * Identificador para serialización
	 */
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
	    response.setContentType("text/html");
	    PrintWriter out = response.getWriter();
	    out.println("<html><head><title>Spring IoC Container: Utilizando los beans fuera del contenedor - Proyecto de prueba </title></head>" + "<body>" + "<h3>HOLA</h3>" + "</body></html>");
	    out.close();
	}

}

El bean a inyectar es el siguiente:

package com.danielme.blog.springioc.obtenerbeans;

/**
 * Bean que será instanciado por Spring IoC.
 * @author http://danielme.com
 *
 */
public class BeanSpring 
{
	/**
	 * Devuelve una cadena de prueba
	 * @return HelloWorld!!
	 */
	public String getMensajeHelloWorld()
	{
		return "Hello World!!";
	}

}

y su definición en el applicationContext.xml

<?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.xsd">

	<bean id="beanSpring" class="com.danielme.blog.springioc.obtenerbeans.BeanSpring"/>


</beans>

Por último el web.xml, se define el servlet, así como el contexto de Spring:

<?xml version="1.0" encoding="UTF-8"?>

<web-app id="springioc" version="2.5"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>springioc</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    
    <servlet>
    	<servlet-name>pruebaSpring</servlet-name>
    	<servlet-class>com.danielme.blog.springioc.obtenerbeans.ServletPrueba</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>pruebaSpring</servlet-name>
    <url-pattern>/spring</url-pattern>
  </servlet-mapping>

   
</web-app>

Podemos importar el proyecto en Eclipse, creando previamente un proyecto web válido con el comando:

$ mvn  eclipse:eclipse -Dwtpversion=2.0
proyecto eclipse

proyecto eclipse

Construyendo el .war y desplegándolo podremos comprobar con un navegador que el servlet está operativo.

proyecto en navegador

proyecto en navegador


Estrategia 1 – Exponer el bean en el ServletContext

Si en la clase en la que queremos recuperar el bean tenemos acceso al ServletContext, Spring permite exponer fácilmente el bean como un atributo del mismo gracias a la clase ServletContextAttributeExporter. El applicationContext.xml queda así:

<?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.xsd">

	<bean id="beanSpring" class="com.danielme.blog.springioc.obtenerbeans.BeanSpring"/>

	<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
	  <property name="attributes">
	    <map>
	      <entry key="beanSpring" value-ref="beanSpring"/>
	    </map>
	  </property>
	</bean>

</beans>

Ahora simplemente recuperamos el bean en nuestro servlet:

@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
	    BeanSpring beanSpring = (BeanSpring) getServletContext().getAttribute("beanSpring");
		
	    StringBuffer html = new StringBuffer();	    
	    html.append("<html><head><title>Spring IoC Container: Utilizando los beans fuera del contenedor - Proyecto de prueba </title></head><body><h3>");
	    html.append(beanSpring.getMensajeHelloWorld());
	    html.append("</h3></body></html>");

	    response.setContentType("text/html");
	    PrintWriter out = response.getWriter();
	    out.write(html.toString());
	    out.close();
	}

Invocando al servelt podemos comprobar que efectivamente tenemos el bean y no se produce un NullPointer.

resultado en el navegador

resultado en el navegador

Estrategia 2 – Utilizar WebApplicationContextUtils

Si, al igual que en el caso anterior, tenemos acceso al ServletContext, se puede utilizar la clase de Spring WebApplicationContextUtils que permite acceder al contexto de Spring asociado al ServletContext.

Para aplicar la estrategia, sólo hay que modificar el servlet:

package com.danielme.blog.springioc.obtenerbeans;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Servlet en el que se recuperará el bean de Spring.
 * @author http://danielme.com
 *
 */
public class ServletPrueba extends HttpServlet {
	
	/**
	 * Identificador para serialización
	 */
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		BeanSpring beanSpring = (BeanSpring) context.getBean("beanSpring");
		
		StringBuffer html = new StringBuffer();	    
	    html.append("<html><head><title>Spring IoC Container: Utilizando los beans fuera del contenedor - Proyecto de prueba </title></head><body><h3>");
	    html.append(beanSpring.getMensajeHelloWorld());
	    html.append("</h3></body></html>");

	    response.setContentType("text/html");
	    PrintWriter out = response.getWriter();
	    out.write(html.toString());
	    out.close();
	}

}

Esta estrategia es evidentemente más intrusiva que la anterior ya que hace al servlet dependiente de Spring

Segunda parte

Código de ejemplo

Descargar desde WordPress.comWordPress: debido a las limitaciones del servicio ofrecido por WordPress.com, el código de ejemplo está comprimido en formato zip pero con la extensión “.odt”, por lo que hay que cambiar la extensión a “.zip” del fichero descargado o abrirlo directamente con el software adecuado.

Descargar desde Ubuntu OneUbuntu One:

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: