Why in the world is my Spring @Autowired Null?

I spent the last month figuring out Spring in a large Java codebase as I tried to convert our build system from Maven to Gradle. We use AspectJ compile time weaving with Spring for dependency injection. When your Spring @Autowired objects are null, it means something is misconfigured.

I must have run into various permutations of this problem a half-dozen times, and each instance took many hours todetermine what was going on. The most highly rated StackOverflow answeris an excellent starting point, but unfortunately, it only solved one iteration of my problem. So I’m going to try to go into extreme detail of everything I learned, in hopes it will save someone time in the future.

To start, here’s a laundry list of things you can try before I go into more detail:

  1. If you’re @Autowiring an instance of Foo inside of Bar, make sure you have a @Component annotation (or @Service, etc) on top of class Foo.
  2. If you’re trying to new an instance of Bar with an @Autowired Foo (without calling SpringContext.getBean()), then you either need to be using AspectJ compile time or load time weaving. This will not work with regular Spring AOP.
  3. Again, if you’re new’ing up an instance of a class, make sure you have the @EnableSpringConfigured annotation on your Spring config, and make sure you have a @Configurable annotation on the class you’re trying to new.
  4. If you’re trying to use load time weaving, you need the @EnableLoadTimeWeaving annotation on your Spring config, and you need to add 2 -javaagent flags (see more details in the Gradle files below) to your JVM:
    1. spring-instrument.jar
    2. aspectjweaver.jar
  5. If you’re encountering @Autowired instances being null only in unit tests:
    1. Check your load orders. I’ve had lots of problemscaused by conflicts between Spring and Powermock.
    2. Make sure you’re always instantiating instances inside of methods instead of the class body or test constructor.
  6. If you’re encountering @Autowired instances being null only within IntelliJ but not when you run your code from the command line using Gradle:
    1. Change the Test Runner in IntelliJ to use Gradle.

Each subsection below details a different problem I encountered, so feel free to jump around. Also note, that in each example, I’m dealing only with Annotation driven Spring configurations. I hate dealing with XML, and I prefer to keep all of my configuration in code.

All of the code detailed below is available here.

1) You’re using ‘new’ on a non-Spring managed object and trying to @Autowire an instance within that class.

This is the main problem linked from the StackOverflow post above.

Essentially, let’s say you have a class Foo with an @Autowired instance variable. If you create Foo by using Foo foo = new Foo(), your Autowired variable won’t work properly. This is because you’re creating an instance of Foo outside of the Spring context, and so Spring can’t help you with any injections. So the simple fix is to call SpringContext.getBean(Foo.class) to get an instance instead of new.

Let’s jump into an example. Below, I create a simple Service and a Component that uses that service, and then I instantiate the Component in main().

And here’s the Gradle build file:

There’s nothing fancy going on here. The Gradle build file doesn’t do anything special. But if you read main(), you’ll notice the call to context.getBean() to instantiate the UserController object. If you choose to uncomment the lines above that, the code will throw a null pointer when the Component tries to access the Autowired service.

This is an example of pure runtime Spring Aspect Oriented Programming or AOP. This is the most limited form of Spring because it’s implemented using runtime proxies. If you need to instantiate classes outside of Spring, or deal with static methods, you’re out of luck.

In those cases, you have two options:

  • Compile time weaving using AspectJ
  • Load time weaving using AspectJ

Compile Time Weaving using AspectJ

Compile time weaving will allow you to instantiate objects using new instead of Spring. To get it to work, we need to make major changes to our build file and add several annotations to the Java files.

The Gradle file changes to:

We pull down an AspectJ plugin which calls the ajc compiler instead of the regularJava compiler which will inject extra functionality into the bytecode.

Then we change our code to:

I’ve only changed 2 lines here. I added @Configurable above the UserController class. And I added @EnableSpringConfigured in my SpringConfig class.

Essentially this tells Spring to look for non-managed classes in addition to the managed ones when doing injections. Note, however, you can’t just add these annotations and expect them to work. You have to use the AspectJ compiler (or load time weaving as we’ll detail later).

If you run the example above, you’ll notice we’re not using Spring to instantiate the UserController() class. We’re able to call new UserController().tryService(), and it works. It doesn’t throw a NullPointer, and the Autowired instance inside of UserController is injected correctly.

For most people, this is all you’ll need. At first I thought it was weird todepend on the AspectJ compiler instead of the regular Java compiler, and I had numerous problems trying to get the Gradle plugin to work. ButI’ve now been running this way for a long time, and we’ve had very few problems with AspectJ.

Load Time Weaving with AspectJ

Let’s say you don’t want to do things at compile time for whatever reason. Instead, you want to try “load time weaving”, which is where AspectJ injects extra bytecode into your Java classon load.

Unfortunately, I think Load Time Weaving is the worst documented option of the 3. Nobody on the internet seems to be using it, and so you’ll be left with only the AspectJ documentation, which can be lacking.

Those warnings aside, here’s our example using load time weaving:

This is quite a bit more complicated than the previous two examples. You need a lot more dependencies, and you have to pass two jars to the jvm at runtime using the -javaagent flag.

Those two jars are:

  • spring-instrument-5.1.2.RELEASE.jar
  • aspectjweaver-1.9.2.jar

Unfortunately, your code WILL NOT work if you forget one or the other. You need both.

And now our Java code changes to:

It’s probably hard to tell, but I only changed a single line of code from the Compile time example. I added @EnableLoadTimeWeaving to our SpringConfig. The -javaagent flags passed on the command line take care of the rest.

Again, we’re able to call new UserController() without using the Spring context and our Autowired service within UserController is injected correctly.

NOTE: I saw many references to aop.xml when I was trying to get load time weaving to work. Ultimately, it wasn’t necessary, at least for this example. I was able to remove mine, and everything still functioned correctly. But I can’t guarantee that if you’re doing something more complex, you won’t need it.

2) Autowired is null in unit tests.

In our project, once we got our code running, we then had to get all of our tests passing using the new injected instances.

The main problem here was a competing load order between Spring and the various static parts of our system.

We relied heavily on Powermock which requires annotations like @PrepareForTest.

Imagine the following class and test code below.

Your test may or may not work in this case because the @Autowired may or may not be null. The @PrepareForTest competes with Spring to instantiate your class.

What’s worse, the test may fail non-deterministically. In our case, it would work when we ran it independently, but it would fail when we put it in a larger suite and the load order changed.

Fortunately, the fix was relatively simple: we moved the @PrepareForTest from the class level to a method containing a @BeforeClass annotation.

A similar problem can happen if you instantiate any instances of your class under test in the class body and not in a function.

For example. if we said

Again, we’re messing with the load order, and bad things may or may not happen. The solution is to instantiate everything inside of test methods with proper @BeforeClass or @BeforeMethod annotations.

3) Autowired null only when running from IntelliJ

Apparently there’s an open bug in IntelliJwhere sometimes partial recompiles using AspectJ don’t work. So your code might work fine from the command line, but then blow up when you try to run a test from IntelliJ.

I encountered this problem when starting my project from Gradle and not from Maven, but I’ve seen reports of the reverse.

There are additional AspectJ plugins available in the full edition of IntelliJ, but I’m only using the Community Edition.

A workaround was to set my test runner to always use Gradle instead of the default “Platform Test Runner”. It’s not pretty, but at least you can guarantee your code is running the same between the command line and your IDE.