The Spring framework, by default, loads and eagerly initializes all beans at the application startup itself. In our application, we might have some pretty resource-intensive beans. We’ll prefer to load such beans on a need basis. We can achieve this using the Spring @Lazy annotation.
In this tutorial, we’ll learn how to use @Lazy annotation to lazily load our beans.
If we mark our Spring configuration class with @Lazy annotation, then all defined beans with @Bean annotation will get lazily loaded:
@Configuration @ComponentScan(basePackages = "com.programmergirl.university") @Lazy public class AppConfig { @Bean public Student student() { return new Student(); } @Bean public Teacher teacher() { return new Teacher(); } }
We can also lazy load just a single bean by using this annotation at the method level:
@Bean @Lazy public Teacher teacher() { return new Teacher(); }
Let’s quickly test out this functionality by running our application:
public class SampleApp { private static final Logger LOG = Logger.getLogger(SampleApp.class); public static void main(String args[]) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); LOG.info("Application Context is already up"); // Beans in our Config class lazily loaded Teacher teacherLazilyLoaded = context.getBean(Teacher.class); Student studentLazilyLoaded = context.getBean(Student.class); } }
On our console, we’ll see:
Bean factory for ...AnnotationConfigApplicationContext: ...DefaultListableBeanFactory: [...] ... Application Context is already up Inside Teacher Constructor Inside Student Constructor
Clearly, Spring initialized both Student and Teacher beans on demand and not while setting up the application context.
We can also use @Lazy annotation at an injection point: constructor, setter or field-level.
Let’s say we have a Classroom class that we want to lazy-load:
@Component @Lazy public class Classroom { public Classroom() { System.out.println("Inside Classroom Constructor"); } ... }
And it’s wired to the University bean via @Autowired annotation:
@Component public class University { @Lazy @Autowired private Classroom classroom; public University() { System.out.println("Inside University Constructor"); } public void useClassroomBean() { this.classroom.getDetails(); ... } }
Here, we have lazily-injected the Classroom bean. And so, while instantiating a University object, Spring will create and map a proxy Classroom object to it. Finally, when we invoke useClassroomBean(), only then it’ll create the actual Classroom instance:
// in our main() method AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); LOG.info("Application Context is already up"); University university = context.getBean(University.class); LOG.info("Time to use the actual classroom bean..."); university.useClassroomBean();
The above code will produce the following logs:
Bean factory for ...AnnotationConfigApplicationContext: ...DefaultListableBeanFactory: [...] ... Inside University Constructor ... Application Context is already up Time to use the actual classroom bean... Inside Classroom Constructor
As we can see, the instantiation of the Classroom object gets delayed until its actually needed.
Please note that, for lazy-injection, we must use @Lazy annotation on both the component class as well as the injection point.
In this quick tutorial, we learned how to lazy load our Spring beans. We talked about lazy initialization and lazy injection.