Spring

Spring @Lazy Annotation

Introduction:

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.

Lazy Initialization:

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();
}

Testing Lazy Loading:

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.

With @Autowired Annotation:

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.

Conclusion:

In this quick tutorial, we learned how to lazy load our Spring beans. We talked about lazy initialization and lazy injection.

Be the First to comment.

Leave a Comment

Your email address will not be published. Required fields are marked *