일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- php 프로그래밍 입문 3판
- Flutter
- C
- 자바 스프링
- php 프로그래밍 입문
- php 프로그래밍 입문 솔루션
- 플러터 개발환경 설정
- programmers
- 자바
- 파이썬
- php 프로그래밍
- spring
- 페이코 추천인코드
- 페이코 초대코드
- Java
- 플러터
- C언어
- php
- 페이코 친구코드
- php 프로그래밍 입문 연습문제
- 배열
- 한정 분기
- php 프로그래밍 입문 문제풀이
- 백준
- 최단 경로
- 페이코 추천인
- 스프링
- php 프로그래밍 입문 예제
- SWEA
- JAVA SPRING
Archives
- Today
- Total
11-16 18:40
ImJay
[Java Spring] 2-36. @Configuration과 싱글톤 본문
반응형
[Java Spring] 2-36. @Configuration과 싱글톤
1. 그런데 이상한점이 있다. 다음 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() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemoryMemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
- memberService 빈을 만드는 코드를 보면 memberRepository() 를 호출한다.
- 이 메서드를 호출하면 new MemoryMemberRepository() 를 호출한다.
- orderService 빈을 만드는 코드도 동일하게 memberRepository() 를 호출한다.
- 이 메서드를 호출하면 new MemoryMemberRepository() 를 호출한다.
결과적으로 각각 다른 2개의 MemoryMemberRepository 가 생성되면서 싱글톤이 깨지는 것 처럼 보인다. 스프링 컨테이너는 이 문제를 어떻게 해결할까?
직접 테스트 해보자.
2. 검증 용도의 코드 추가
package hello.core.member;
public class MemberServiceImpl implements MemberService{
// private final MemberRepository memberRepository = new MemoryMemberRepository();
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
public class OrderServiceImpl implements OrderService {
// private final MemberRepository memberRepository = new MemoryMemberRepository();
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
테스트를 위해 MemberRepository를 조회할 수 있는 기능을 추가한다. 기능 검증을 위해 잠깐 사용하는 것이니 인터페이스에 조회기능까지 추가하지는 말자.
3. 테스트 코드
package hello.core.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;
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 -> memberRepository = " + memberRepository1);
System.out.println("orderService -> memberRepository = " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
4. 테스트 코드 빌드
- 확인해보면 memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용된다.
- AppConfig의 자바 코드를 보면 분명히 각각 2번 new MemoryMemberRepository 호출해서 다른 인스턴스가 생성되어야 하는데?
- 어떻게 된 일일까? 혹시 두 번 호출이 안되는 것일까? 실험을 통해 알아보자.
5. 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("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemoryMemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
스프링 컨테이너가 각각 @Bean을 호출해서 스프링 빈을 생성한다. 그래서 memberRepository() 는 다음과 같이 총 3번이 호출되어야 하는 것 아닐까?
- 1. 스프링 컨테이너가 스프링 빈에 등록하기 위해 @Bean이 붙어있는 memberRepository() 호출
- 2. memberService() 로직에서 memberRepository() 호출
- 3. orderService() 로직에서 memberRepository() 호출
6. 테스트케이스 빌드
그런데 출력 결과는 모두 1번만 호출된다.
왜 그런걸까? 다음 글을 통해 알아보자.
반응형
'Java Spring > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
[Java Spring] 2-38. 컴포넌트 스캔과 의존관계 자동 주입 시작하기 (1) | 2023.03.14 |
---|---|
[Java Spring] 2-37. @Configuration과 바이트코드 조작의 마법 (0) | 2023.03.13 |
[Java Spring] 2-35. 싱글톤 방식의 주의점 (0) | 2023.03.13 |
[Java Spring] 2-34. 싱글톤 컨테이너 (0) | 2023.03.13 |
[Java Spring] 2-33. 싱글톤 패턴 (0) | 2023.03.13 |
Comments