Primavera junit4 dilema Manual- / auto-cableado
-
30-09-2019 - |
Pregunta
me encontré con un problema que sólo puede explicarse con mi falta fundamental de comprensión de instalaciones de contenedores y el contexto de configuración de la COI de la primavera, así que me gustaría pedir una aclaración al respecto.
Sólo como referencia, una aplicación que estoy maintaing tiene la siguiente pila de tecnologías:
- Java 1.6
- Spring 2.5.6
- RichFaces 3.3.1 GA-IU marco
- Spring se utiliza para la gestión de frijol con módulo de resorte JDBC utilizado para el apoyo DAO
- Maven se utiliza como gestor de la creación
- JUnit 4.4 se introduce ahora como motor de prueba
Soy retroactivamente (sic!) Escribir las pruebas JUnit para la aplicación y lo sorprendió mí es que yo no era capaz de inyectar un grano en una clase de prueba mediante inyección de setter sin tener que recurrir a la notación @Autowire.
Permítanme ofrecer configurar un ejemplo y que acompañan a los archivos de configuración.
El TypeTest
clase de prueba es muy simple:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {
@Autowired
private IType type;
@Test
public void testFindAllTypes() {
List<Type> result;
try {
result = type.findAlltTypes();
assertNotNull(result);
} catch (Exception e) {
e.printStackTrace();
fail("Exception caught with " + e.getMessage());
}
}
}
Su contexto se define en TestStackOverflowExample-context.xml
:
<context:property-placeholder location="classpath:testContext.properties" />
<context:annotation-config />
<tx:annotation-driven />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.connection.driver.class}" />
<property name="url" value="${db.connection.url}" />
<property name="username" value="${db.connection.username}" />
<property name="password" value="${db.connection.password}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="beanDAO" class="com.example.BeanDAOImpl">
<property name="ds" ref="dataSource"></property>
<property name="beanDAOTwo" ref="beanDAOTwo"></property>
</bean>
<bean id="beanDAOTwo" class="com.example.BeanDAOTwoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="type" class="com.example.TypeImpl">
<property name="beanDAO" ref="beanDAO"></property>
</bean>
TestContext.properties
está en la ruta de clase y sólo contiene datos db-específicos necesarios para fuente de datos.
Esto funciona como un encanto, pero mi pregunta es - ¿por qué no funciona cuando trato de los granos de alambre manual y realizo inyección de setter como en:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {
private IType type;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
¿Qué me estoy perdiendo aquí? ¿Qué parte de la configuración está mal aquí? Cuando trato de introducir manualmente los granos a través de los emisores, prueba falla porque esta parte
result = type.findAlltTypes();
se resuelve como nulo en tiempo de ejecución. He, por supuesto, consulté la referencia Primavera manual y probado varias combinaciones de configuración XML; todo lo que pude concluir es que la primavera no fue capaz de inyectar los granos, ya que de alguna manera no adecuada eliminar la referencia de pruebas de la primavera de referencia Contexto pero usando @Autowired esto sucede "automágicamente" y realmente no se puede ver por qué es que debido a JavaDoc tanto de anotación y Autowired
su clase PostProcessor
no menciona esto.
También vale la pena añadir es el hecho de que @Autowired
se utiliza en la aplicación sólo aquí. En otras partes se realiza sólo cableado manual, por lo que esta cuestión también trae sucesivamente - ¿por qué está trabajando no no aquí , en mi prueba? ¿Qué parte de la configuración DI me estoy perdiendo? ¿Cómo @Autowired
referencia Get Contexto de la primavera?
EDIT: También he intentado esto, pero con el mismo resultado:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
¿Alguna otra idea, tal vez?
Edit2:
He encontrado una manera sin tener que recurrir a la escritura propia TestContextListener
o BeanPostProcessor
. Es sorprendentemente simple y resulta que yo estaba en el camino correcto con mi última edición:
1) contexto basado en Constructor resolver:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
type = ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
2) Mediante la implementación de la interfaz ApplicationContextAware:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
type = (Type) ctx.getBean("type");
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
Ambos enfoques granos Instanced correctamente.
Solución
Si se echa un vistazo a la fuente de org.springframework.test.context.support.DependencyInjectionTestExecutionListener
, verá el siguiente método (con formato y comentado por claridad):
protected void injectDependencies(final TestContext testContext)
throws Exception {
Object bean = testContext.getTestInstance();
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext()
.getAutowireCapableBeanFactory();
beanFactory.autowireBeanProperties(bean,
AutowireCapableBeanFactory.AUTOWIRE_NO,
// no autowiring!!!!!!!!
false
);
beanFactory.initializeBean(bean, testContext.getTestClass().getName());
// but here, bean post processors are run
testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}
Por lo tanto el objeto de prueba es un grano sin auto-cableado. Sin embargo, @AutoWired
, @Resource
etc, no utilice el mecanismo autowiring, utilizan BeanPostProcessor
. Y así, las dependencias se inyectan si y sólo si se utilizan las anotaciones (o si se registra algún otro BeanPostProcessor
que lo hace).
(El código anterior es de 3.0.x Spring, pero apuesto que era la misma en 2.5.x)