Spring은 기업용 온라인 서비스 기술을 지원을 위해 개발됨
DI 컨테이너 : 직접 만들었던 AppConfig
문제1. 요청이 있으면 객체를 new로 생성해서 전달 = 요청하는 횟수만큼 생성됨
memberService1: hello.core.member.MemberServiceImpl@305b7c14
memberService2: hello.core.member.MemberServiceImpl@6913c1fb
호출할 때마다 새로운 것이 생성됨. 생성된 객체의 참조값이 다른 것을 확인할 수 있다.
메모리 낭비가 심함 => 생성된 객체 인스턴스를 공유해서 사용하자
하나의 서비스에서 하나의 인스턴스가 생성되는 것을 보장하는 디자인 패턴 = 싱글톤패턴
private으로 설정했기 때문에 외부에서 임의로 객체를 생성할 수 없는 것을 확인할 수 있다.
static 영역에 객체 instance를 미리 하나 생성해서 올려주고
getInstance()로만 조회
컴파일 오류로 에러를 잡는 것이 잘 설계한 것이다.
@Test
@DisplayName("싱글톤 패턴을 사용한 객체 사용")
void SingletonServiceTest(){
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println("singletonService1 : "+singletonService1);
System.out.println("singletonService2 : "+singletonService2);
Assertions.assertThat(singletonService1).isSameAs(singletonService2);
}
singletonService1 : hello.core.singleton.SingletonService@22fcf7ab
singletonService2 : hello.core.singleton.SingletonService@22fcf7ab
isSame : 인스턴스
isEquls : 값
스프링 컨테이너를 사용하면 싱글톤 패턴이 적용된다.
getInstance로 가져와서 하지 않아도 된다
==> 유연성 떨어지고 안티패턴, 자식 클래스 만들기 어려움
싱글톤 컨테이너
싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리
컨테이너는 객체를 하나만 생성해서 관리함=싱글톤 레지스트리(싱글톤 객체 생성하고 관리하는 기능)
DIP, OCP, 테스트, private 모두 사용가능
memberService1: hello.core.member.MemberServiceImpl@5829e4f4
memberService2: hello.core.member.MemberServiceImpl@5829e4f4
memberService1: hello.core.member.MemberServiceImpl@1698fc68
memberService2: hello.core.member.MemberServiceImpl@1698fc68
같은 객체를 사용하는 것을 확인할 수 있다.
싱글톤 방식의 주의점!
특정 클라이언트에 의존적이면 안 된다
특정 클라이언트가 값을 변경할 수 없도록
공유필드를 사용할 때는 스프일 빈을 항상 무상태 stateless로 설계해야 한다.
public class StatefulService {
//private int price;
public int order(String name, int price){
System.out.println("name: "+name+" price : "+price);
//this.price = price;
return price;
}
public void getPrice(){
//return price;
}
}
@Configuration
@Bean이 있는데 memberService 호출되면 memberRepository 호출
orderService호출되면 memberRepository호출됨
==> 싱글톤이 깨질까 깨지지 않을까
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);
}
}
memberService -> memberRepository1: hello.core.member.MemoryMemberRepository@708400f6
orderService -> memberRepository2: hello.core.member.MemoryMemberRepository@708400f6
memberRepository: hello.core.member.MemoryMemberRepository@708400f6
//call AppConfig.memberService
//call AppConfig.memberRepository
//call AppConfig.memberRepository
//call AppConfig.orderService
//call AppConfig.memberRepository
//call AppConfig.memberService
//call AppConfig.memberRepository
//call AppConfig.orderService
@Configuration과 바이트코드 조작의 마법
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean: "+bean.getClass());
}
bean: class hello.core.AppConfig$$EnhancerBySpringCGLIB$$c329221c
bean: class hello.core.AppConfig
$$EnhancerBySpringCGLIB : CGLIB 바이트코드 조작 라이브러리
$$c329221c : 클래스 정보
순수한 클래스
class hello.core.AppConfig
스프링이 빈을 등록할 때 "EnhancerBySpringCGLIB"라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고 그 다른 클래스를 스프링 빈으로 등록한 것!
instance 객체가 AppConfig@CGLIB 로 등록되어있다.
스프링 컨테이너에 등록되어 있으면 찾아서 반환, 없으면 생성하고 스프링 컨테이너에 등록하는 로직을 실행
@Configuration을 사용하지 않고 @Bean만 사용하면
순수한 클래스로 등록되고 싱글톤을 보장하지 않는다.
@Configuration을 사용하면 아래와 같이 스프링 컨테이너에 등록된 bean을 조회해서 사용할 수 있지만
@Bean
public MemberService memberService(){
// 생성자 주입
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());// 배우
}
@Configuration을 사용하지 않으면 객체를 새로 생성해서 사용하는 것과 같다.
@Bean
public MemberService memberService(){
// 생성자 주입
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(new MemoryMemberRepository());// 배우
}
@Autowired를 사용해서 의존관계주입을 해주면 스프링에 등록된 bean을 가져올 수 있다.
@Autowired MemberRepository memberRepository;
@Bean
public MemberService memberService(){
// 생성자 주입
System.out.println("call AppConfig.memberService")
return new MemberServiceImpl(memberRepository);// 배우
}
@Configuration과 @Bean의 조합으로 싱글톤을 보장하는 경우는 정적이지 않은 메서드일 때입니다.
정적 메서드에 @Bean을 사용하게 되면 싱글톤 보장을 위한 지원을 받지 못합니다.
By marking this method as static, it can be invoked without causing instantiation of its declaring @Configuration class, thus avoiding the above-mentioned lifecycle conflicts. Note however that static @Bean methods will not be enhanced for scoping and AOP semantics as mentioned above.
'프로젝트 하면서 > spring' 카테고리의 다른 글
Spring Batch (0) | 2023.04.23 |
---|---|
[SpringBoot] IntelliJ로 시작하는 SpringBoot 프로젝트 설정 (0) | 2021.07.23 |
[java] cmd에서 java 실행하기 (2) | 2021.06.23 |
[PO] ProObject 설치 (3) (0) | 2021.06.22 |
[po7] HotDeployGlobalDeployVersionNotValidWhilePreparing (1) | 2021.06.01 |
댓글