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.

¿Fue útil?

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)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top