Backend Development 10 min read

Mastering Spring’s @Lazy: Speed Up Startup and Solve Circular Dependencies

This article introduces Spring's @Lazy annotation, explains its purpose and scenarios such as improving startup speed, breaking circular dependencies, and correctly injecting prototype beans, and provides five practical code examples while also announcing a continuously updated Spring Boot 3 case collection with a PDF ebook and source code.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring’s @Lazy: Speed Up Startup and Solve Circular Dependencies

The post announces a Spring Boot 3 practical case collection (over 80 articles) that will be permanently updated, offering a PDF ebook and source code for subscribers.

1. Introduction

The @Lazy annotation in Spring enables lazy (deferred) loading of beans. It can be applied to classes, methods, constructors, parameters, and fields. Using @Lazy delays bean initialization until the bean is actually needed, which can improve startup performance and resolve certain dependency issues.

1.1 Purpose

By default, Spring initializes all singleton beans at container startup, which may involve heavy operations such as network I/O or complex calculations. Annotating a bean with @Lazy postpones its initialization until it is first requested, reducing startup time and resource consumption.

1.2 Application Scenarios

Improve system startup speed : When many beans perform costly initialization, marking them with @Lazy can significantly speed up startup. For example, a bean that reads a large cache from Redis can be lazy‑loaded so the application starts quickly and only accesses Redis when needed.

Resolve circular dependencies : If two beans depend on each other via constructor injection, Spring may encounter a circular‑dependency error. Adding @Lazy to one of the constructors breaks the cycle.

Inject prototype beans into a singleton : When a singleton bean needs a new instance of a prototype bean each time, annotating the prototype injection point with @Lazy ensures a fresh proxy is created for each use.

2. Practical Examples

2.1 Environment Setup

<code>public class PersonDAO {}</code><code>public class PersonService {</code><code>    private PersonDAO dao;</code><code>    public String toString() {</code><code>        return "PersonService [dao=" + dao.getClass() + "]";</code><code>    }</code><code>}</code><code>// Test entry</code><code>try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {</code><code>    context.register(PersonDAO.class);</code><code>    context.register(PersonService.class);</code><code>    context.refresh();</code><code>    System.out.println(context.getBean(PersonService.class));</code><code>}</code>

All following examples build on the classes above.

2.2 Field Injection

<code>// @Resource</code><code>// @Autowired</code><code>@Lazy</code><code>private PersonDAO dao;</code>

Output:

<code>PersonService [dao=class com.pack.PersonDAO$$SpringCGLIB$$0]</code>

The field annotated with @Lazy is injected as a proxy, regardless of whether @Resource or @Autowired is used.

Note: When using GenericApplicationContext , @Autowired is not effective by default. You need to set a ContextAnnotationAutowireCandidateResolver on the bean factory:
<code>DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();</code><code>beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());</code>

2.3 Method Injection

<code>@Resource</code><code>@Lazy</code><code>public void setPersonDAO(PersonDAO dao) {</code><code>    this.dao = dao;</code><code>}</code>

Output shows a proxy instance.

<code>PersonService [dao=class com.pack.PersonDAO$$SpringCGLIB$$0]</code>

2.4 Constructor Injection

<code>@Lazy</code><code>public PersonService(PersonDAO dao) {</code><code>    this.dao = dao;</code><code>}</code>

Output:

<code>PersonService [dao=class com.pack.PersonDAO$$SpringCGLIB$$0]</code>

The @Lazy annotation can also be placed on the constructor parameter:

<code>public PersonService(@Lazy PersonDAO dao) {</code><code>    this.dao = dao;</code><code>}</code>

2.5 Singleton Injecting Prototype Bean

<code>@Scope("prototype")</code><code>public class PersonDAO {}</code>
<code>public class PersonService {</code><code>    @Autowired</code><code>    private PersonDAO dao;</code><code>    public void save() {</code><code>        System.out.printf("PersonDAO hashCode: %s%n", dao);</code><code>    }</code><code>}</code>

Without @Lazy , the same prototype instance is reused:

<code>PersonDAO hashCode: com.pack.PersonDAO@66565121</code>

Adding @Lazy to the field yields a new instance each call:

<code>PersonDAO hashCode: com.pack.PersonDAO@73a2e526</code><code>PersonDAO hashCode: com.pack.PersonDAO@13f95696</code><code>PersonDAO hashCode: com.pack.PersonDAO@68be8808</code>

2.6 Circular Dependency

<code>class A {</code><code>    private B b;</code><code>    public A(B b) { this.b = b; }</code><code>}</code><code>public class B {</code><code>    private A a;</code><code>    public B(A a) { this.a = a; }</code><code>}</code>

Using constructor injection for both classes causes a circular‑dependency error. Adding @Lazy to one constructor resolves the issue:

<code>public A(@Lazy B b) { this.b = b; }</code>

Only one side needs the @Lazy annotation.

Overall, the article demonstrates the definition, benefits, and five common usage patterns of the @Lazy annotation in Spring, complemented by a promotional note about the continuously updated Spring Boot 3 case collection.

JavaSpring Bootdependency injectionlazy-loadingLazy
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.