스프링부트

[Spring Boot 3 & Spring Framework 6 마스터하기!] SpringFramework 고급 기능 살펴보기(1)

jinaenya 2025. 3. 20. 15:18

※ 해당 강의 내용을 필기하고 실습 시 트러블슈팅하는 내용으로 구성됨

※ 강의는 Eclipse 환경이지만, 해당 게시물에선 intelliJ 환경에서 작업

 

 

문제 디버깅을 위한 도움말

 

master-spring-and-spring-boot/01-spring/step-by-step-code-changes/step-by-step-guide.md at main · in28minutes/master-spring-and

Spring and Spring Boot Tutorial For Absolute Beginners - 10-in-1 - Spring to Spring Boot to REST API to Full Stack to Containers to Cloud - in28minutes/master-spring-and-spring-boot

github.com

 


package com.in28minutes.learn_spring_framework.examples.d1;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.ComponentScan;

import java.util.Arrays;

@Component
class ClassA {
    // Bean을 생성하고 있는 ClassA
}

@Component
class ClassB {
    // ClassA Bean을 사용하여 초기화하는 ClassB
    //classB에서 수행되는 초기화 논리
    private ClassA classA;

    public ClassB(ClassA classA){
        // 여기에 자동 작업이 나타나게 됨
        //의존성 주입해야 함.
        //Logic
        System.out.println("Some Initialization logic");
        this.classA = classA;
    }
}

// public class가 아니면,
// @ComponentScan을 안 붙이면,
// Component scan이 안 되면서 초기화 인식안됨
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
    public static void main(String[] args){
        try(var context =
                    new AnnotationConfigApplicationContext
                            (LazyInitializationLauncherApplication.class)){
        }
    }
}

 

 

  • public class LazyInitializationLauncherApplication에서는 ClassB Bean을 사용하지 않고 있다. Spring Context를 실행하면 초기화가 자동으로 이뤄진 것임.
  • @ComponentScan과 public 표기 잊을 경우 초기화 인식이 제대로 안 됨!! 유의.
  • 즉, Bean을 로드하지 않고 Bean에서 메서드를 호출하지 않아도, 자동으로 Bean이 초기화
    • @Lazy를 ClassB 상단에 적어 적용하면 이걸 방지할 수 있음. 초기화 논리가 실행되지 않고 Bean 초기화가 일어나지 않음. ClassB Bean은 시작할 때가 아니라 ClassB Bean을 사용할 때 초기화됨.

 

자동 초기화를 @Lazy로 방지한 모습

 

@Component
class ClassA {
    // Bean을 생성하고 있는 ClassA
}

@Component
@Lazy
class ClassB {
    // ClassA Bean을 사용하여 초기화하는 ClassB
    //classB에서 수행되는 초기화 논리
    private ClassA classA;

    public ClassB(ClassA classA){
        // 여기에 자동 작업이 나타나게 됨
        //의존성 주입해야 함.
        //Logic
        System.out.println("Some Initialization logic");
        this.classA = classA;
    }
    public void doSomething(){
        System.out.println("Do Something");
    }
}

// public class가 아니면,
// @ComponentScan을 안 붙이면,
// Component scan이 안 되면서 초기화 인식안됨
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
    public static void main(String[] args){
        try(var context =
                    new AnnotationConfigApplicationContext
                            (LazyInitializationLauncherApplication.class)){
            System.out.println("Initialization of context is completed");
            context.getBean(ClassB.class).doSomething();
        }
    }
}

 

 

 

ClassB 내부 method를 사용하게끔 하면서, 초기화 로직->loading the Bean -> 사용 하는 흐름이 됨.

즉, ClassB를 사용/참조하려고 할 때만 로드되는 게 Lazy annotaion의 역할임.

  • @Lazy : Bean을 지연하여 초기화할지 여부를 나타냄.
  • @Component를 사용하는 모든 클래스타 Bean에 어노테이션을 적용한 모든 메서드에 적용할 수 있음.

 

정리

  • Eager initialization(recommended)
    • 스프링 Bean에 대한 기본 초기화 방법
    • Bean은 시작할 때 즉시 초기화됨
    • configuration에서의 에러를 애플리케이션 시작하자마자 발견할 수 있기에 추천
  • Lazily Initialization(@Lazy)(비추천, 자주 쓰이지도 않음)
    • @Component와 @Bean을 쓰는 거의 모든 곳에서 사용 가능
    • Lazy-resolution proxy가 실제 dependency 대신 주입됨
    • @Configuration class 에서도 사용 가능
      • 그렇게 되면, 모든 @Bean method들이 lazily initialized.
    • 복잡한 초기화 논리가 많고 시작 시 지연시키고 싶지 않은 상황이라면 고려해볼만 함.
    • application에 Bean이 드물게 사용될 때 사용 가능. 사용할 때만 Bean을 가져올 때.
    • 주의
      • 사용 시, application 시작 시 spring configuration error는 발생되지 않고, runtime error만 확인 가능할 것.


package com.in28minutes.learn_spring_framework.examples.e1;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
class NormalClass{


}

@Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
class PrototypeClass{

}

@Configuration
@ComponentScan
public class BeanScopesLauncherApplication {
    public static void main(String[] args){
        try(var context =
                    new AnnotationConfigApplicationContext
                            (BeanScopesLauncherApplication.class)){
            // Scope가 없는 클래스
            System.out.println(context.getBean(NormalClass.class));
            System.out.println(context.getBean(NormalClass.class));
            // 해시코드를 보았을 때, 기본적으로 동일한 Bean이 다시 검색됨
            // 반환되는 NormalClass의 instance는 같다.

            // Scope가 있는 클래스
            System.out.println(context.getBean(PrototypeClass.class));
            System.out.println(context.getBean(PrototypeClass.class));
            System.out.println(context.getBean(PrototypeClass.class));
            // context.getBean을 호출할 때마다 새로운 해시코드 값이 나타남.
            // PrototypeClass는 새로운(다른) instance를 얻음
            // 매번 이 spring context에서 새로운 Bean을 가져오는 것
        }
    }
}

  • SpringFramework에서 생성되는 모든 Bean은 기본적으로 Singleton
    • Bean을 요청할 때마다 같은 인스턴스 반환(e.g. NormalClass)
  • 하지만 Bean을 참조/요청할 때마다 매번 다른 인스턴스를 생성하고 싶은 경우에는 Prototype을 쓰면 됨(e.g.PrototypeClass)
    • @Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE)

 

정리 : Spring Beans는 특정 scope로 사용되어 정의됨

  • Singleton : Spring IoC 컨테이너 당 객체 인스턴스가 하나
  • Prototype : Spring IoC 컨테이너 당 객체 인스턴스가 여러 개(일 수 있음)

  • 웹 애플리케이션에서만 특정하게 적용되는 scopes in web-aware ApplicationContext
    • Request : HTTP request 하나 당 하나의 객체 인스턴스가 생성됨
    • Session
      • 사용자와 관련되어 있음. 동일 사용자에게 속하는 여러 번의 같은 세션에 속해있을 수 있음.
      • user HTTP Session당 하나의 객체 인스턴스가 생성됨
    • Application
      • web application runtime 당 하나의 객체 인스턴스가 생성됨.
      • 즉, 웹 애플리케이션 전체에 객체 인스턴스가 하나인 것임.
    • Websocket
      • WebSocket 인스턴스 당 객체 인스턴스가 하나
  • Java Singleton(GOF) vs Spring Singleton
    • Spring Singleton : Spring IoC 컨테이너당 객체 instance 한 개
    • Java Singleton(GOF) : JVM 당 객체 instance 하나
    • 유의 사항
      • JVM에 Spring IoC 컨테이너를 하나만 실행한다면 Spring 싱글톤과 Java 싱글톤은 같은 의미(99%의 경우)
      • 하지만 JVM에 Spring IoC 컨테이너 여러 개를 실행하면 Spring 싱글톤은 Java 싱글톤과 달라짐
        • 보통은 JVM에 Spring IoC 컨테이너 여러 개를 실행하지는 않음