[6/21 TIL] SPRING(Circular Dependency, @ComponentScan, @Autowired, 복수 개의 빈 설정, Bean Scope, Lifecycle Callbacks)

yumyeonghan·2023년 6월 22일
0

🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃

Circular Dependency

  • 스프링 빈 간에 서로 의존하면서 상호적인 관계가 형성되는 상황
  • A 객체가 B 객체를 의존하고, 동시에 B 객체가 A 객체를 의존하는 경우를 의미
  • BeanCurrentlyInCreationException 예외가 발생

예시 코드

public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public B(A a) {
        this.a = a;
    }
}

@ComponentScan

  • 스프링이 직접 클래스를 검색(스캔)해서 빈으로 등록해주는 기능
  • @Configuration이 적용된 AppConfig 클래스에 직접 @Bean으로 등록하지 않아도 원하는 클래스를 빈으로 등록 가능
  • 스프링은 자동으로 등록될 빈을 Stereotype 애노테이션을 이용해서 스캔함
  • 처음 만들어지는 (ProjcetName)Application 클래스의 @SpringBootApplication에 @ComponentScan이 있어서 루트 설정 클래스가 되므로 별도의 AppConfig 클래스를 생성하지 않아도됨

스트레오타입


그림 출처 바로가기

@Component

  • 스프링의 컴포넌트 스캔에 의해 자동으로 빈으로 등록됨
  • 일반적으로 클래스 수준에서 사용되며, 해당 클래스를 스프링의 관리 대상으로 지정

@Repository

  • 데이터 액세스 계층의 구현체인 리포지토리를 나타냄
  • 주로 데이터베이스 액세스와 관련된 작업을 수행하는 클래스에 적용
  • 예를 들어, 데이터베이스의 CRUD(Create, Read, Update, Delete) 작업을 처리하는 DAO(Data Access Object) 클래스를 @Repository로 지정

@Service

  • 비즈니스 로직을 수행하는 서비스 클래스를 나타냄
  • 주로 비즈니스 로직을 구현하는 메서드가 포함된 클래스에 적용

@Controller

  • 스프링 MVC에서 웹 애플리케이션의 컨트롤러를 나타냄
  • HTTP 요청을 처리하고 응답을 반환하는 역할을 수행

@Configuration

  • 스프링 빈 설정 파일임을 나타냄
  • 해당 클래스의 메서드에 @Bean 애노테이션을 사용하여 스프링 빈을 정의

스캔 범위

@Configuration
@ComponentScan(basePackages = {"com.example.package1", "com.example.package2"})
public class AppConfig {
    // ...
}
  • basePackages 속성을 사용해서 스캔할 패키지 경로를 지정
  • 해당 패키지에 있는 모든 빈들을 검색
@Configuration
@ComponentScan(basePackageClasses = {Component1.class, Component2.class})
public class AppConfig {
    // ...
}
  • basePackageClasses 속성을 사용해서 스캔할 클래스 지정
  • 해당 클래스가 속한 패키지의 모든 빈들을 검색

@Autowired

  • @Autowired를 사용하면 스프링은 해당 필드나 메서드 매개변수의 타입에 맞는 빈을 찾아서 자동으로 주입해줌
  • 즉, 의존성을 명시적으로 선언하지 않고도 스프링 컨테이너가 자동으로 관리하는 빈을 주입받을 수 있음
  • 생성자 주입, setter 주입, 필드 주입 방법 등이 있지만 스프링은 생성자 주입 방법을 권장함

생성자 주입

  • 초기화시에 필요한 모든 의존관계가 형성되기 때문에 안전함
    • 객체의 생성과 초기화가 한 번에 이루어지므로, 필요한 의존 관계가 모두 주입되지 않은 상태로 객체를 사용하는 오류를 방지함
    • 이는 런타임 시점에서 발생할 수 있는 NullPointerException 등의 예외를 방지함
  • 잘못된 패턴을 찾을 수 있음
    • 생성자 주입을 사용하면 클래스가 어떤 의존 관계를 필요로 하는지 명확하게 드러남
    • 예를 들어, 빠진 의존성이나 중복된 의존성 등을 빠르게 감지하고 수정 가능
  • 테스트를 쉽게 해줌
    • 생성자 주입은 의존 관계를 외부에서 주입받기 때문에 테스트할 때 모의 객체(Mock Object)를 주입하여 스프링 없이 독립적으로 테스트가 가능
    • 생성자 주입을 통해 의존 관계를 명시적으로 주입받기 때문에 테스트에서 필요한 의존 관계를 쉽게 제어하고 조작 가능
  • 불변성을 확보
    • 생성자 주입을 사용하면 한 번 설정된 의존 관계는 변경되지 않는 불변성(final)을 가짐
    • 의존 관계가 외부에서 주입되기 때문에 해당 클래스 내에서는 해당 의존 관계를 변경할 수 없으며, 이는 다중 스레드 환경에서도 안전성을 보장
  • 만약, 생성자가 하나일 경우 @Autowired는 생략 가능하지만 둘 이상이면 지정해줘야함
public class VoucherService {

    private final VoucherRepository voucherRepository;

	//생성자가 하나라면, @Autowired 생략 가능
    public VoucherService(VoucherRepository voucherRepository) {
        this.voucherRepository = voucherRepository;
    }
}

복수 개의 빈 설정

  • 스프링은 무슨 객체를 주입해야하는지 모르기 때문에 자동 주입이 가능한 빈이 두개 이상이면 어떤 빈이 주입되야하는지 선택해야함

@Primary

  • @Primary 어노테이션이 적용된 빈은 다른 동일한 타입의 빈보다 우선적으로 주입됨
  • 클래스 레벨 또는 메서드 레벨에 적용
@Component
@Primary
public class EmailSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}

@Component
public class SmsSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

@Qualifier

  • @Qualifier 어노테이션은 주입할 빈을 선택하기 위한 한정자로, 해당 빈의 이름이나 식별자를 지정
  • 주로 메서드 매개변수 또는 필드 레벨에서 @Autowired와 함께 사용
@Component
public class MessageService {
    private final MessageSender messageSender;

    public MessageService(@Qualifier("emailSender") MessageSender messageSender) {
        this.messageSender = messageSender;
    }

    public void sendMessage(String message) {
        messageSender.sendMessage(message);
    }
}

Bean Scope


그림 출처 바로가기

@Bean
@Scope("prototype")
public MyBean myBean() {
    return new MyBean();
}
  • Singleton (Default)

    • Singleton 스코프는 기본 스코프로, 애플리케이션 컨텍스트 내에서 단일 인스턴스를 생성하고 공유
    • 같은 이름의 빈을 여러 번 요청해도 항상 동일한 인스턴스를 반환
    • 개발자가 별도의 설정을 하지 않으면 모든 빈은 Singleton 스코프로 생성
  • Prototype

    • Prototype 스코프는 요청할 때마다 새로운 인스턴스를 생성
    • Prototype 스코프의 빈은 애플리케이션 컨텍스트에서 관리되지 않으며, 해당 빈에 대한 관리는 개발자에 있음
  • Request

    • Request 스코프는 웹 애플리케이션에서 HTTP 요청 당 하나의 인스턴스를 생성
    • 각각의 HTTP 요청마다 독립적인 인스턴스를 생성하므로 다중 스레드 환경에서 안전하게 사용 가능
    • 웹 환경에서만 사용 가능 (Spring MVC, Spring WebFlux 등)
  • Session

    • Session 스코프는 웹 애플리케이션에서 HTTP 세션 당 하나의 인스턴스를 생성
    • 각각의 HTTP 세션마다 독립적인 인스턴스를 생성하므로 사용자별로 유지되어야 하는 데이터를 관리하는 데 유용
    • 웹 환경에서만 사용 가능 (Spring MVC, Spring WebFlux 등)
  • Application

    • Application 스코프는 애플리케이션 전체에 걸쳐 단일 인스턴스를 생성
    • 애플리케이션 시작부터 종료까지 동일한 인스턴스를 유지하므로, 애플리케이션 전반적으로 공유되어야 하는 상태를 유지하는 데 사용
    • 웹 환경에서만 사용 가능 (Spring MVC, Spring WebFlux 등)
  • websocket

    • 웹 소켓과 동일한 생명주기를 가지는 스코프
    • 웹 환경에서만 사용 가능 (Spring MVC, Spring WebFlux 등)

Bean Lifecycle Callbacks

  • 빈(Bean)의 라이프사이클(Callbacks)은 빈이 생성되고 초기화되는 과정과 소멸되는 과정을 의미
  • 스프링은 빈이 생성될 때 필요한 초기화 작업이나 소멸될 때 정리 작업을 수행 가능

Bean 생성 생명주기 콜백 방법

  1. @PostConstruct 애노테이션이 적용된 메소드 호출(스프링 권장)
  2. Bean이 InitializingBean 인터페이스 구현시 afterPropertiesSet 호출
  3. @Bean 애노테이션의 initMethod 에 설정한 메소드 호출

Baen 소멸 생명주기 콜백 방법

  1. @PreDestory 애노테이션이 적용된 메소드 호출(스프링 권장)
  2. Bean이 DisposableBean 인터페이스 구현시 destroy 호출
  3. @Bean 애노테이션의 destroyMethod 에 설정한 메소드 호출

예시 코드

@Component
public class MyBean {
    @PostConstruct
    public void init() {
        // 빈 초기화 작업 수행
        System.out.println("MyBean initialized");
    }
    
    @PreDestroy
    public void cleanup() {
        // 정리 작업 수행
        System.out.println("MyBean cleaned up");
    }
}
profile
웹 개발에 관심 있습니다.

0개의 댓글