Thursday, April 8, 2010

Make @Configurable work with Spring 2.5.6 without @DirtiesContext

At a recent project we finally went for compile time weaving and bumped into the following issue: @Configurable injects sometimes the incorrect version of a certain dependency.

Spring's @Configurable is based upon a static aspect.

Hence you might end up in the following scenario:
  • Test1 uses Ctx1
  • Test2 uses Ctx2
  • Test3 uses Ctx1
In case the execution order of the tests is Test1, Test2, Test3 and no test is marked with @DirtiesContext then what happens is:
  • Test1 runs

    • Ctx1 is not in the context cache of spring

    • Ctx1 is loaded and the aspect is initialized with that context

    • Ctx1 is put into the context cache of spring

  • Test2 runs
    • Ctx2 is not in the context cache of spring

    • Ctx2 is loaded and the aspect is initialized with that context

    • Ctx2 is put into the context cache of spring


  • Test3 runs
    • Ctx1 is inside the context cache of spring

    • Ctx1 is retrieved from the cache and the aspect is still pointing to Ctx2


We solved this issue by reinitializing the context the aspect is pointing to, by extending the SpringJUnit4ClassRunner and registering a custom TestExecutionListener




package x.y.z;

import org.aspectj.lang.Aspects;
import org.junit.internal.runners.InitializationError;
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractTestExecutionListener;

public class MySpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {

public MySpringJUnit4ClassRunner(Class clazz) throws InitializationError {
super(clazz);
getTestContextManager().registerTestExecutionListeners(new AbstractTestExecutionListener() {
@Override
public void prepareTestInstance(TestContext testContext)
throws Exception {
Aspects.aspectOf(AnnotationBeanConfigurerAspect.class).destroy();
Aspects.aspectOf(AnnotationBeanConfigurerAspect.class)
.setBeanFactory(((GenericApplicationContext)testContext.getApplicationContext()).getDefaultListableBeanFactory());

}
});
}

}

2 comments:

  1. apparently it's already been picked up in jira of spring :)

    ReplyDelete
  2. Added Aspects.aspectOf(AnnotationBeanConfigurerAspect.class).destroy();
    before reconfiguring.

    ReplyDelete