21‐Lazy‐Initialization - JulianeMaran32/Java-with-Spring-Boot GitHub Wiki

49. Lazy Initialization - Visão Geral

🔄 Inicialização dos Beans

  • Por padrão, quando sua aplicação Spring inicia, todos os beans são criados imediatamente.
  • Isso inclui todos os beans anotados com @Component, @Service, @Repository, @Controller, etc.
  • O Spring cria uma instância de cada bean e os deixa prontos para uso.

🛠 Diagnóstico: adicionando println no construtor

@Component
public class CricketCoach implements Coach {

    public CricketCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

}
  • getClass().getSimpleName() imprime o nome da classe, útil para ver quais beans estão sendo instanciados.

▶ Quando iniciamos a aplicação:

In constructor: BaseballCoach  
In constructor: CricketCoach  
In constructor: TennisCoach  
In constructor: TrackCoach  
  • Isso mostra que todos os beans são criados na inicialização, mesmo que não sejam usados ainda.

💤 O que é Lazy Initialization?

  • Lazy Initialization significa que o bean só será criado quando for realmente necessário.
  • Um bean só é inicializado se:
    • For usado em uma injeção de dependência.
    • For solicitado diretamente no código.

🔧 Usando @Lazy em uma classe

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Lazy
@Component
public class TrackCoach implements Coach {

    public TrackCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

}

✅ Injetando apenas o CricketCoach

@RestController
public class DemoController {

    private Coach myCoach;

    @Autowired
    public DemoController(@Qualifier(value = "cricketCoach") Coach myCoach) {
        this.myCoach = myCoach;
    }

}
  • Nesse exemplo, o TrackCoach não será inicializado, pois não está sendo injetado.

Saída esperada:

In constructor: BaseballCoach  
In constructor: CricketCoach  
In constructor: TennisCoach  

🧩 E os outros beans?

  • Se quiser aplicar Lazy Initialization a outros beans, será necessário adicionar @Lazy em cada classe.
  • Isso pode se tornar trabalhoso em aplicações grandes.

🌐 Configuração Global com application.properties

Para ativar Lazy Initialization para todos os beans, use:

spring.main.lazy-initialization=true
  • Agora, nenhum bean será criado até que seja realmente necessário.
  • Até mesmo o DemoController só será criado quando sua rota for acessada.

🖨 Adicionando println no DemoController

@Component
public class CricketCoach implements Coach {

    public CricketCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

}

@RestController
public class DemoController {

    private Coach myCoach;

    @Autowired    
    public DemoController(@Qualifier(value = "cricketCoach") Coach myCoach) {
        System.out.println("In constructor: " + getClass().getSimpleName());
        this.myCoach = myCoach;
    }
}

🔁 Como funciona o carregamento?

  1. O usuário acessa o endpoint REST /dailywork.
  2. O Spring identifica que precisa do DemoController.
  3. Para criar o DemoController, ele precisa do CricketCoach.
  4. Então o Spring cria primeiro o CricketCoach, depois o DemoController.

Saída esperada:

In constructor: CricketCoach  
In constructor: DemoController  

⚖️ Vantagens e Desvantagens

✅ Vantagens

  • Cria os objetos somente quando são usados.
  • Pode melhorar o tempo de inicialização da aplicação, especialmente se ela tiver muitos beans.

⚠️ Desvantagens

  • Beans como @RestController não serão criados até serem chamados pela primeira vez.
  • Problemas de configuração só serão detectados no momento da criação, e não na inicialização.
  • É preciso garantir que o sistema tenha memória suficiente para os beans quando forem carregados.

🧠 Dica final

  • Lazy Initialization está desativado por padrão.
  • Antes de ativá-lo, teste e analise sua aplicação.
  • Evite a otimização prematura — só use se realmente trouxer benefícios.

50. Lazy Initialization - Codificação - Parte 1

Etapas desta aula:

  1. Remover a anotação @Primary
  2. Utilizar @Qualifier para definir qual implementação será injetada
  3. Adicionar prints nos construtores para observar a criação dos objetos

Exemplo de código:

@RestController
public class DemoController {

    private Coach myCoach;

    @Autowired
    public DemoController(@Qualifier("cricketCoach") Coach myCoach) {
        System.out.println("In constructor: " + getClass().getSimpleName());
        this.myCoach = myCoach;
    }

    @GetMapping("/dailyworkout")
    public String getDailyWorkout() {
        return myCoach.getDailyWorkout();
    }
}
@Component
public class BaseballCoach implements Coach {
    public BaseballCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

    @Override
    public String getDailyWorkout() {
        return "Spend 30 minutes in batting practice";
    }
}
@Component
public class CricketCoach implements Coach {
    public CricketCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

    @Override
    public String getDailyWorkout() {
        return "Practice fast bowling for 15 minutes";
    }
}
@Component
public class TennisCoach implements Coach {
    public TennisCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

    @Override
    public String getDailyWorkout() {
        return "Practice your backhand volley";
    }
}
@Component
public class TrackCoach implements Coach {
    public TrackCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

    @Override
    public String getDailyWorkout() {
        return "Run a hard 5k!";
    }
}

Resultado da execução:

Quando executamos o projeto e acessamos o endpoint http://localhost:8080/dailyworkout, o Spring cria e carrega todas as classes marcadas com @Component, mesmo que nem todas estejam sendo utilizadas.

O log mostra a seguinte saída:

In constructor: BaseballCoach
In constructor: CricketCoach
In constructor: TennisCoach
In constructor: TrackCoach
In constructor: DemoController

51. Lazy Initialization - Codificação - Parte 2

Tornando uma classe "preguiçosa" com @Lazy

Se queremos que um bean só seja criado quando for realmente necessário, podemos usar a anotação @Lazy.

@Lazy
@Component
public class TrackCoach implements Coach {
    public TrackCoach() {
        System.out.println("In constructor: " + getClass().getSimpleName());
    }

    @Override
    public String getDailyWorkout() {
        return "Run a hard 5k!";
    }
}

Resultado após aplicar @Lazy:

Se a classe TrackCoach não está sendo usada diretamente em nenhum lugar (não é injetada), o Spring não irá instanciá-la:

In constructor: BaseballCoach
In constructor: CricketCoach
In constructor: TennisCoach
In constructor: DemoController

Ou seja, o TrackCoach não aparece no log — pois o Spring não precisou dele.

Inicialização preguiçosa global

Se quisermos que todos os beans sejam carregados somente quando forem utilizados, podemos configurar isso no arquivo application.properties:

spring.main.lazy-initialization=true

Resultado:

Com essa configuração, o Spring só cria os beans no momento exato em que forem necessários:

In constructor: CricketCoach
In constructor: DemoController

Isso acontece porque o DemoController precisa do CricketCoach, então apenas esses dois foram instanciados. Os outros beans (BaseballCoach, TennisCoach, TrackCoach) não foram carregados porque não foram utilizados.

Conclusão

  • Por padrão, o Spring cria todos os beans na inicialização da aplicação.
  • Com @Lazy, podemos indicar que um bean só deve ser criado quando for realmente necessário.
  • Também é possível ativar a inicialização preguiçosa globalmente, com a propriedade spring.main.lazy-initialization=true.
  • Isso pode melhorar o tempo de inicialização da aplicação e economizar recursos, especialmente em aplicações grandes.