@Configuration
- Bean을 등록할 때 싱글톤이 되도록 보장해 주는 어노테이션
- 스프링 컨테이너에서 Bean을 관리할 수 있게 됨
- 결론적으로 스프링 설정 정보는 항상 @Configuration 사용
AppConfig
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MemberService memberService(){
System.out.println("AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemoryMemberRepository memberRepository() {
System.out.println("AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
System.out.println("AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy(){
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
Test
package hello.singleton;
import hello.core.AppConfig;
import hello.core.member.MemberRepository;
import hello.core.member.MemberServiceImpl;
import hello.core.order.OrderServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
public class ConfigurationSingletonTest {
@Test
void configurationTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository1 = " + memberRepository1);
System.out.println("orderService -> memberRepository2 = " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
결과
바이트코드 조작
- 스프링이 CGLIP라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스 생성
- 생성된 다른 클래스를 스프링 빈으로 등록. 해당 클래스가 바로 싱글톤이 보장되도록 해줌
AppConfig@CGLIB 예상 코드
@Bean
public MemberRepository memberRepository() {
if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else { //스프링 컨테이너에 없으면 기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
}
- @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환
- 없으면 생성해서 스프링 빈으로 등록하고 반환
- 이로 인해 싱글톤이 보장
- @Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지는 않음
'Spring > Basic' 카테고리의 다른 글
의존관계 자동 주입 (0) | 2024.08.29 |
---|---|
@ComponentScan (0) | 2024.08.21 |
Singleton (0) | 2024.08.20 |
스프링 컨테이너 (0) | 2024.08.17 |
AppConfig (0) | 2024.08.15 |