Diferencia entre revisiones de «Inicializacion Lazy De Spring Para Tests»
Línea 13: | Línea 13: | ||
</code> | </code> | ||
− | Esto es muy útil durante los tests de componentes ya que nos permite en el test | + | Esto es muy útil durante los tests de componentes ya que nos permite inicializar e involucrar en el test solamente los beans relacionados. |
− | El problema es que | + | El problema que surge es que es conveniente utilizar los mismos archivos de configuración que irán a producción. Así estaremos testeando que la configuración de Spring sea correcta. |
− | |||
− | + | Imaginemos los siguientes archivos de configuración: | |
− | business | + | app-business.xml |
<code xml> | <code xml> | ||
<beans> | <beans> | ||
Línea 31: | Línea 30: | ||
</code> | </code> | ||
− | dao | + | app-dao.xml |
<code xml> | <code xml> | ||
<beans> | <beans> | ||
Línea 42: | Línea 41: | ||
</code> | </code> | ||
− | jms | + | app-jms.xml |
<code xml> | <code xml> | ||
<beans> | <beans> | ||
Línea 53: | Línea 52: | ||
</code> | </code> | ||
− | + | Un test de componentes comenzaría con algo así: | |
+ | |||
+ | <code java> | ||
+ | @ContextConfiguration(locations = | ||
+ | "classpath:app-business.xml", | ||
+ | "classpath:dao-business.xml", | ||
+ | "classpath:jms-business.xml" | ||
+ | ) | ||
+ | public class BlahComponenteTest extends AbstractJUnit4SpringContextTests { | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Si queremos que la inicialización sea lazy, cada uno de los archivos de configuración debería comenzar con: | ||
+ | <code xml> | ||
+ | <beans default-lazy-init=true> | ||
+ | </code> | ||
+ | |||
+ | Esto implica que tengamos que tener archivos alternativos para los tests: | ||
+ | <code java> | ||
+ | @ContextConfiguration(locations = | ||
+ | "classpath:app-business-componente-test.xml", | ||
+ | "classpath:dao-business-componente-test.xml", | ||
+ | "classpath:jms-business-componente-test.xml" | ||
+ | ) | ||
+ | public class BlahComponenteTest extends AbstractJUnit4SpringContextTests { | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Una solución que suponemos que debería funcionar es importar los tres archivos en otro y hacer a este lazy: | ||
+ | |||
− | |||
<code xml> | <code xml> | ||
<beans default-lazy-init="true"> | <beans default-lazy-init="true"> | ||
Línea 64: | Línea 93: | ||
</code> | </code> | ||
− | + | Y después lo incluímos como un sólo archivo: | |
<code java> | <code java> | ||
Línea 76: | Línea 105: | ||
</code> | </code> | ||
− | + | El problema es que la propiedad default-lazy-init no se propaga por las configuraciones importadas (Verificar si esto sigue siendo así en versiones más nuevas de Spring). | |
− | + | ===Posible Solución=== | |
+ | Una posible solución para esto es crear un ContextLoader propio y especificarlo en la anotación @ContextConfiguration. | ||
− | + | El ContextLoader utilizará un DefinitionDocumentReader que le seteará a cualquier definición que lea, el atributo DEFAULT_LAZY_INIT_ATTRIBUTE como true. | |
<code java> | <code java> | ||
Línea 117: | Línea 147: | ||
} | } | ||
+ | </code> | ||
+ | |||
+ | Y así quedaría el test en cuestión: | ||
+ | <code java> | ||
+ | |||
+ | @ContextConfiguration( | ||
+ | loader = "" | ||
+ | locations = "classpath:app-spring-config-componente-test.xml" | ||
+ | ) | ||
+ | public class BlahComponenteTest extends AbstractJUnit4SpringContextTests { | ||
+ | ... | ||
+ | } | ||
</code> | </code> |
Revisión del 18:21 6 oct 2009
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 inicializar e involucrar en el test solamente los beans relacionados.
El problema que surge es que es conveniente utilizar los mismos archivos de configuración que irán a producción. Así estaremos testeando que la configuración de Spring sea correcta.
Imaginemos los siguientes archivos de configuración:
app-business.xml
<beans>
... <bean id="UnBo" ...> ... <bean id="OtroBo" ...> ...
</beans>
app-dao.xml
<beans>
... <bean id="UnDao" ...> ... <bean id="OtroDao" ...> ...
</beans>
app-jms.xml
<beans>
... <bean id="UnJmsTemplate" ...> ... <bean id="OtroJmsTemplate" ...> ...
</beans>
Un test de componentes comenzaría con algo así:
@ContextConfiguration(locations =
"classpath:app-business.xml", "classpath:dao-business.xml", "classpath:jms-business.xml" )
public class BlahComponenteTest extends AbstractJUnit4SpringContextTests {
...
}
Si queremos que la inicialización sea lazy, cada uno de los archivos de configuración debería comenzar con:
<beans default-lazy-init=true>
Esto implica que tengamos que tener archivos alternativos para los tests:
@ContextConfiguration(locations =
"classpath:app-business-componente-test.xml", "classpath:dao-business-componente-test.xml", "classpath:jms-business-componente-test.xml" )
public class BlahComponenteTest extends AbstractJUnit4SpringContextTests {
...
}
Una solución que suponemos que debería funcionar es importar los tres archivos en otro y hacer a este lazy:
<beans default-lazy-init="true">
<import resource="app-business.xml" /> <import resource="app-dao.xml" /> <import resource="app-jms.xml" />
</beans>
Y después lo incluímos como un sólo archivo:
@ContextConfiguration(locations =
"classpath:app-spring-config-componente-test.xml" )
public class BlahComponenteTest extends AbstractJUnit4SpringContextTests {
...
}
El problema es que la propiedad default-lazy-init no se propaga por las configuraciones importadas (Verificar si esto sigue siendo así en versiones más nuevas de Spring).
Posible Solución
Una posible solución para esto es crear un ContextLoader propio y especificarlo en la anotación @ContextConfiguration.
El ContextLoader utilizará un DefinitionDocumentReader que le seteará a cualquier definición que lea, el atributo DEFAULT_LAZY_INIT_ATTRIBUTE como true.
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); }
Y así quedaría el test en cuestión:
@ContextConfiguration(
loader = "" locations = "classpath:app-spring-config-componente-test.xml" )
public class BlahComponenteTest extends AbstractJUnit4SpringContextTests {
...
}