@Bean??
개발자가 직접 객체를 생성하여 스프링 컨테이너에 등록할 때 사용하는 어노테이션이다.
설정 클래스의 메서드 위에 보통 붙여서 특정 객체를 빈으로 등록할 때 사용된다.
public class AppConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
}
간단히 serviceA() 메서드는 스프링 컨테이너에 ServiceA 객체를 빈으로 등록한다.
@Configuration??
설정 클래스임을 명시하는 어노테이션이다. 내부적으로 CGLIB 프록시가 적용되어서 @Bean 메서드 간의 호출을 감싸주어 싱글톤을 보장한다.
CGLIB??
- CGLIB(Code Generation Library)는 바이트코드를 조작하여 클래스 기반의 프록시 객체를 생성하는 라이브러리이다.
- Spring에서 AOP 적용 시, 인터페이스가 없으면 CGLIB을 사용하여 프록시 객체를 만든다. (기본적으로 JDK 동적 프록시 사용)
- 메서드 오버라이딩 방식으로 동작하기 때문에 final 클래스나 final 메서드에는 적용할 수 없다.
@Configuration을 사용할 때와 사용하지 않을 때의 차이
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA(serviceB());
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
Configuration이 적용되면 serviceB()가 여러 번 호출되어도 스프링 컨테이너에 등록된 동일한 ServiceB 빈을 반환한다.
만약 @Bean만 사용했을 경우는?
serviceB()가 호출될 때마다 새로운 객체가 생성되므로 싱글톤이 깨질 수 있다.
테스트 코드:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ServiceA serviceA1 = context.getBean(ServiceA.class);
ServiceA serviceA2 = context.getBean(ServiceA.class);
ServiceB serviceB1 = context.getBean(ServiceB.class);
ServiceB serviceB2 = context.getBean(ServiceB.class);
serviceA1.printServiceB();
serviceA2.printServiceB();
System.out.println("Bean으로 가져온 ServiceB: " + serviceB1);
System.out.println("Bean으로 가져온 ServiceB: " + serviceB2);
이러면 출력 결과 serviceB 빈은 싱글톤이지만 ServiceA 내부에서 주입된 ServiceB는 서로 다른 객체가 된다.
@Bean만 단독으로 사용해도 되는 경우
1. 컴포넌트 스캔을 사용하는 경우
스프링에선 @Component, @Service, @Repository 등의 어노테이션을 사용해서 빈을 자동으로 등록할 수 있다.
@Bean을 단독으로 사용해도 문제없이 빈이 컨테이너에 등록된다.
이 방식이 가능한 이유??
- @Component가 붙은 클래스 자체가 빈으로 등록되기 때문에 내부의 @Bean 메서드도 자동으로 등록된다.
2. 메서드 간의 호출 관계가 없는 경우
@Bean 메서드 간의 호출 관계가 없다면 @Configuration이 없어도 싱글톤이 깨질 위험이 없다.
각각 독립적인 객체를 생성할 뿐이므로 프록시를 사용할 필요가 없다.
public class AppConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
각 @Bean이 서로 의존하지 않는다면 @Configuration 없이도 문제 없다.
@Bean
public ServiceA serviceA() {
return new ServiceA(serviceB());
}
이런 식으로 서로 호출하면 문제가 생기지만 그렇지 않으면 된다.
'Backend > Spring Boot' 카테고리의 다른 글
스프링 빈의 생명주기 콜백에 대해서 (0) | 2025.02.12 |
---|---|
@Autowired 필드 명, @Qualifier, @Primary 정리 (0) | 2025.02.11 |
@Controller와 @RestController 차이 (1) | 2025.02.04 |
싱글톤 패턴 (0) | 2024.10.14 |
스프링 컨테이너와 스프링 빈 (1) | 2024.10.13 |