🍃프로그래머스 백엔드 데브코스 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;
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 생성 생명주기 콜백 방법
- @PostConstruct 애노테이션이 적용된 메소드 호출(스프링 권장)
- Bean이 InitializingBean 인터페이스 구현시 afterPropertiesSet 호출
- @Bean 애노테이션의 initMethod 에 설정한 메소드 호출
Baen 소멸 생명주기 콜백 방법
- @PreDestory 애노테이션이 적용된 메소드 호출(스프링 권장)
- Bean이 DisposableBean 인터페이스 구현시 destroy 호출
- @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");
}
}