JMH Benchmark get NullPointerException with Autowired field in Spring(with maven) project
Asked Answered
D

2

8

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?

Decide answered 4/5, 2017 at 9:14 Comment(1)
did you ever find a solution?Aphra
D
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.

Damalus answered 24/1, 2022 at 12:48 Comment(0)
M
-1

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);
Mazurka answered 30/7, 2017 at 10:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.