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
Great! Works well. Thank you for the information.
ReplyDeleteNo worries, glad I could help. Thanks.
ReplyDelete