'JMH Benchmark get NullPointerException with Autowired field in Spring(with maven) project
I try to benchmark some of the methods of my Spring (with maven) project. I need to use @Autowired and @Inject on several fields in my project. While I run my project, it works well. But JMH always gets NullPointerException with @Autowired/@Inject fields.
public class Resources {
private List<Migratable> resources;
@Autowired
public void setResources(List<Migratable> migratables) {
this.resources = migratables;
}
public Collection<Migratable> getResources() {
return resources;
}
}
My Benchmark class
@State(Scope.Thread)
public class MyBenchmark {
@State(Scope.Thread)
public static class BenchmarkState {
Resources res;
@Setup
public void prepare() {
res = new Resources();
}
}
@Benchmark
public void testBenchmark(BenchmarkState state, Blackhole blackhole) {
blackhole.consume(state.res.getResources());
}
}
When I run my benchmark, it get NullPointerException at Resources.getResources()
More specifically at resources.
It cannot Autowire setResources(). But if I run my project(exclude benchmark), it works fine.
How can I get rid of this NullPointerException with Autowired field while benchmarking?
Solution 1:[1]
Here is an example of how to run Spring-based benchmarks: https://github.com/stsypanov/spring-boot-benchmark.
Basically what you need is to store a reference to your application context as a field of benchmarks class, initialize the context in @Setup method and close it in @TearDown. Something like this:
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(value = Mode.AverageTime)
public class ProjectionVsDtoBenchmark {
private ManyFieldsRepository repository;
private ConfigurableApplicationContext context;
@Setup
public void init() {
context = SpringApplication.run(Application.class);
context.registerShutdownHook();
repository = context.getBean(ManyFieldsRepository.class);
}
@TearDown
public void closeContext(){
context.close();
}
}
The logic that you are going to measure must be encapsulated in a method of Spring component called from @Benchmark annotated method. Remember general rules of benchmarking to make sure your measurements are correct, e.g. use Blackhole or return value from the method to prevent compiler from DCE.
Solution 2:[2]
Try to use
@RunWith(SpringJUnit4ClassRunner.class) and @ContextConfiguration(locations = {...}) on the test class. This should initialize Spring TestContext Framework and let you autowire dependencies.
If this doesn't work, then you have to start Spring ApplicationContext explicitly as a part of you @Setup annotated method, using either of
ClassPathXmlApplicationContext, FileSystemXmlApplicationContext or WebXmlApplicationContext and resolve beans from that context:
ApplicationContext context = new ChosenApplicationContext("path_to_your_context_location");
res = context.getBean(Resources.class);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
