|

Spring series, part 4: @Lazy on injection points

Last time, when I talked about lazily loaded beans I forgot to mention one interesting feature of Spring 4 – lazily loaded injection points. This post introduces the combination of @Lazy and @Autowire as a counterpart to the standard approach (JSR-330) using @Inject and Provider.

As of Spring 4.0 @Lazy can be applied on injection points, such as @Autowire or @Inject. From the docs:
 
In addition to its role for component initialization, this annotation may also be placed on injection points marked with Autowired or Inject: In that context, it leads to the creation of a lazy-resolution proxy for all affected dependencies, as an alternative to using ObjectFactory or Provider.
Let’s start with a bit of a background. The Provider interface is part of JSR-330 and, among other usages, it allows for breaking circular dependencies and deals with lazy instantiation in general.
 
A quick explanatory example, a mere copy-paste from the docs:
class Car {
   @Inject Car(Provider seatProvider) {
     Seat driver = seatProvider.get();
     Seat passenger = seatProvider.get();
     ...
   }
}

Okay, but how exactly would it be implemented outside of a JEE container? Well, here is a simple Spring application.

Start with adding a Maven dependency:

<dependency>
  <groupid>javax.inject</groupid>
  <artifactid>javax.inject</artifactid>
  <version>1</version>
</dependency>

Next, define the model with indirection in mind (Provider, @Inject):

public class Seat {..}
import javax.inject.Inject;
import javax.inject.Provider;
public class Car {
   private Seat driver;
   private Seat passenger;
   @Inject
   public Car(Provider‹Seat› seatProvider) {
     driver =  seatProvider.get();
     passenger = seatProvider.get();
   }
}

Now, and here is where the magic happens, make use of the Spring application context capabilities:

@Component
public class Car {..}
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.context.annotation.Scope;
  import javax.inject.Provider;  

  @Configuration
  public class SeatProvider implements Provider‹Seat› {
    @Override
    @Bean
    @Scope(value = ″prototype″)
    public Seat get() {
      return new Seat();
    }
}

Finally, try it out:

// Unit tests
  ..
   @Autowired
   private Car car;
   ..
   // Instantiation should happen
   assertNotNull(car.getDriver());
   assertNotNull(car.getPassenger());
   // The passenger and the driver shouldn't
   // share the same (prototype-scoped) seat
   assertFalse(car.getDriver().equals(car.getPassenger()));
 }

All of the tests pass and, with a little help of Spring (application context, prototypes), the dependency injection is mediated via the Provider.

As of Spring 4.0, the same goal can be achieved by simply using @Lazy alongside with @Autowire. Here is how our example would change when using Spring only:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;  

@Component(value = ″lazySeat″)
@Scope(value = ″prototype″)
public class Seat {..}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;  

@Component(value = ″lazyCar″)
public class Car {
  @Autowired @Lazy
  private Seat driver;  

  @Autowired @Lazy
  private Seat passenger;
}

That’s it. The same tests would pass again and more importantly the eager load of Seats would never happen, thanks to adding @Lazy to the @Autowire injection point.

Source Code

Previous: Part 3 – @Lazy Next: Part 5 – @Component vs @Bean

Similar Posts