Contenido[ocultar] |
<script type="text/javascript"></script>
Spring provee una forma simple y cómoda para crear EJBs de Stateless, Stateful y Message Driven Bean. Esto se hace heredando la implementación de los EJB de alguna de estas clases:
Estas clases proveen varios beneficios:
Las clases brindan un factory de Spring ya creado e inicializado, el cual se accede a través del método heredado getBeanFactory(). Este factory se configura a través de variables de entorno en el archivo ejb-jar.xml.
El método ejbCreate() se encuentra implementado (para poder inicializar el factory y otras tareas). Spring provee el método onEjbCreate() el cual implementaremos para inyectar "a mano" las dependencias de nuestro EJB.
Supondremos que ya contamos con un objeto de negocio FlotaEspacialBO de la siguiente forma:
public class FlotaEspacialBoImpl implements FlotaEspacialBo { public Collection<Invasor> buscarInvasores(Long idFlota) { ... buscar los invasores de la flota indicada ... } }
Expondremos este EJB (el cual lo tenemos ya declarado en Spring) usando un EJB Stateless. Para el EJB crearemos todas las interafaces necesarias (Home, Remote, Local, etc). La diferencia estará en el bean:
public class FlotaEspacialBean extends AbstractStatelessSessionBean { private FlotaEspacialBo flotaEspacialBo; public Collection<Invasor> buscarInvasores(Long idFlota) { return flotaEspcialBo.buscarInvasores(idFlota); } @Override protected void onEjbCreate() throws CreateException { flotaEspcialBo = (FlotaEspacialBo) getBeanFactory().getBean("business.FlotaEspacialBo"); } }
Como se ve, el bean hereda de !AbstractStatelessSessionBean, clase que ya provee varios métodos implementados para los EJB. Así, el EJB queda muy simple, invocando al objeto de negocio directamente para resolver la lógica. El método onEjbCreate() se encarga de inyectar las dependencias del EJB utilizando el método getBeanFactory() que viene heredado.
Falta entonces indicarle a Spring los archivos de configuración que deberá levantar el factory, lo cual va en el ejb-jar.xml
<ejb-jar> <enterprise-beans> <session> <ejb-name>FlotaEspacialBean</ejb-name> <home>com.dosideas.business.ejb.flota.FlotaEspacialRemoteHome</home> <remote>com.dosideas.business.ejb.flota.FlotaEspacialRemote</remote> <ejb-class>com.dosideas.business.ejb.flota.FlotaEspacialBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <env-entry> <env-entry-name>ejb/BeanFactoryPath</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>application-ejb.xml,application-negocio.xml</env-entry-value> </env-entry> </session> </enterprise-beans> </ejb-jar>
Los tag env-entry contienen la ubicación de los archivos de Spring para inicializar.
EJB 3 contiene muchas mejoras y simplificaciones al momento de crear EJBs. Integrar EJB3 y Spring es una tarea muy sencilla, que se resuelve con el uso de Anotaciones.
import javax.interceptor.Interceptors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor; @Stateless @Interceptors(SpringBeanAutowiringInterceptor.class) public class PaisSessionBeanBean implements PaisSessionBeanRemote { @Autowired private PaisBo paisBo; public Pais buscarPaisPorId(Long id) { return paisBo.buscarPaisPorId(id); } }
La anotacion @Interceptors(SpringBeanAutowiringInterceptor.class) prepara un factory para ser usado en el EJB. A su vez, se encarga de inyectar todos los atributos marcados con @Autowired.
El interceptor busca un archivo beanRefContext.xml en el classpath, que contenga una instancia de un ApplicationContext, el cual referencia a los archivos de Spring a cargar. Por ejemplo:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean name="applicationContext-main" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg> <list> <value>application-business.xml</value> <value>application-dao.xml</value> <value>application-db.xml</value> <value>application-hibernate.xml</value> </list> </constructor-arg> </bean> </beans>
Uno de los usos más interesantes es la posibilidad de dejar a Spring realizar el lookup del EJB e inyectar la interfaz de negocio directamente en nuestros objetos.
Así, delegamos a Spring la localización y creación del EJB.
Para esto, se utilizan 2 clases principales:
Este ejemplo funciona con EJB 2.x.
Supongamos que tenemos un EJB llamado PersonaServiceBean. Esta EJB está compuesto de las siguiente clases:
En un archivo de Spring, podemos declarar una referencia a nuestro EJB, que luego podremos inyectar como un bean normal.
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"/> <bean id="ejb.PersonaServiceBean" class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean"> <property name="jndiName"> <value>ejb/PersonaServiceBean</value> </property> <property name="jndiTemplate"> <ref local="jndiTemplate"/> </property> <property name="businessInterface"> <value>com.dosideas.business.ejb.persona.PersonaServiceRemote</value> </property> </bean> </beans>
El bean con id ejb.PersonaServiceBean es un proxy que cumple con la interfaz indicada en el atributo "businessInterface". En el ejemplo, se declaró la interfaz de negocio remota del EJB. Este bean puede ya ser inyectado en cualquier otro lado y utilizado normalmente.
Spring realizará la creación del bean igual que con el resto de los objetos. En este caso, al iniciarse el factory de Spring se realizará el lookup e instanciación correspondiente del EJB. Es posible demorar esta acción con los medios tradicionales que provee Spring (lazy-init, etc.).
El atributo businessInterface indica la interfaz que implementará el Proxy de Spring. Un detalle interesante es que esta interfaz no tiene porqué ser la interfaz de negocio del EJB.
Así, podriamos crear una interfaz "nuestra", que cumpla con los métodos que están en la interfaz de negocio del EJB. De esta manera, nuestra aplicación queda independiente de cambios en la interfaz del EJB (y la posiblidad, además, de intercambiar libremente entre interfaces remotas y locales del EJB).
Este ejemplo funciona con EJB 3.x.
Supongamos que tenemos un EJB llamado PersonaServiceBean. Esta EJB está compuesto por la siguiente interfaz:
En un archivo de Spring, podemos declarar una referencia a nuestro EJB, que luego podremos inyectar como un bean normal.
<?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-2.0.xsd"> <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate" /> <bean id="ejb.PersonaServiceBean" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="ejb/PersonaServiceBean"/> </bean> </beans>
Una forma alternativa es utilizando los tags propios para jee que trae Spring.
<jee:jndi-lookup id="ejb.PersonaServiceBean" jndi-name="ejb/PersonaServiceBean" cache="true" />
Si por algún motivo se reinicia el servidor de EJBs, podría ser necesario reiniciar también a los clientes.
Podemos usar el atributo refresh-home-on-connect-failure para forzar una nueva búsqueda de nuestro cliente en el caso de un error de conexión. Para EJB 3.0, esto funciona a partir de Spring 2.5.5 (ya que las versiones anteriores contenían un bug).
<jee:remote-slsb id="miServicio" jndi-name="ejb/MiServicio" business-interface="com.dosideas.business.ejb.MiServicio" cache-home="false" lookup-home-on-startup="false" home-interface="com.dosideas.business.ejb.MiServicioHome" resource-ref="false" refresh-home-on-connect-failure="true"> <jee:environment> <!-- Incluir información del entorno aquí --> </jee:environment> </jee:remote-slsb>