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.
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