Inicializacion Lazy De Spring Para Tests
Spring provee un mecanismo por el cual se le puede indicar que un bean sea consturído lo más tardíamente posbile.
De esta manera el bean no se instanciará hasta que no se necesite en el código en tiempo de ejecución en forma directa o porque es la dependencia de otro bean que es necesario.
Se puede utilizar a nivel de bean:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
o a nivel del contenedor
<beans default-lazy-init="true">
...
</beans>
Esto es muy útil durante los tests de componentes ya que nos permite en el test utilizar la configuración lo más similar a la que existirá en producción. Si marcamos el contenedor como lazy en el test de componentes no se instanciarán beans que no se usen directa o indirectamente durante el test, evitando que fallen porque pueden existir componentes que no estén listos, o que estén fallando pero que no participen en los tests, o porque tengan una dependencia con un entorno inexistente.
El problema es que en la versión 2.5 de Spring (habria que verificar si en alguna sub versión posterior o en la 3.x esto fue cambiado) cuando importamos recursos para componer varios xml en uno sólo, la propiedad lazy no se propaga por los imports. Esto hace que no sea posible hacer el contenedor lazy a menos que reemplacemos todos los archivos de configuración involucrados. Pero en este caso perderíamos la ventaja que buscábamos: tener los archivos de configuración lo más similar a los de producción.
Por ejemplo, si tuviésemos los siguientes archivos de configuración en el proyecto:
business
<beans>
... <bean id="UnBo" ...> ... <bean id="OtroBo" ...> ...
</beans>
dao
<beans>
... <bean id="UnDao" ...> ... <bean id="OtroDao" ...> ...
</beans>
jms
<beans>
... <bean id="UnJmsTemplate" ...> ... <bean id="OtroJmsTemplate" ...> ...
</beans>
podríamos incluirlos todos en un archivo de configuración general para los tests
app-spring-config-componente-test.xml
<beans default-lazy-init="true">
<import resource="app-business.xml" /> <import resource="app-dao.xml" /> <import resource="app-jms.xml" />
</beans>
y lo usamos en un test de la siguiente manera:
@ContextConfiguration(locations =
"classpath:app-spring-config-componente-test.xml" )
public class BlahComponenteTest extends AbstractJUnit4SpringContextTests {
...
}
Si funcionase como imaginamos, todos los beans (a menos que se haya indicado lo contrario), se inicilizarïan sólo si se usan durante el test en cuestión.
El problema es que el atributo default-lazy-init no se propaga a las configuraciones de business, dao y jms que están improtadas.
Una solución para esto es definir en la anotación @ContextConfiguration un ContextLoader propio con un leve cambio en la forma en que levanta la configuración.
package com.tm.cma.web.base;
import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.beans.factory.xml.XmlReaderContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.test.context.support.GenericXmlContextLoader; import org.w3c.dom.Element;
public class LazyContextLoader extends GenericXmlContextLoader {
protected BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context) { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(context); beanDefinitionReader.setDocumentReaderClass(LazyBeanDocumentReader.class); return beanDefinitionReader; }
}
class LazyBeanDocumentReader extends DefaultBeanDefinitionDocumentReader {
protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) { root.setAttribute( BeanDefinitionParserDelegate.DEFAULT_LAZY_INIT_ATTRIBUTE, "true" ); return super.createHelper(readerContext, root); }