준비
이전 게시물인 [Spring] 간단한 서비스 생성 및 테스트에서 작성된 코드가 있어야 본 글의 코드 예제을 수행할 수 있습니다.
스프링 없는 컨테이너만 사용한 객체 생성 방법
sample_Project패키지에 singletonTest.java 클래스를 생성합니다.
package developingman.sample_Project;
import developingman.sample_Project.member.MemberService;
public class SingletonTest {
static DependencyConfig dependencyConfig = new DependencyConfig();
static MemberService memberService1 = dependencyConfig.memberService();
static MemberService memberService2 = dependencyConfig.memberService();
public static void main(String[] args){
System.out.println("memberService1 : " + memberService1);
System.out.println("memberService2 : " + memberService2);
}
}
아래와 같은 결과를 확인할 수 있습니다.
같은 memberService를 사용하지만 뒤에 붙은 주소값이 다른 것을 확인할 수 있습니다.
수많은 객체를 생성하게 되면 위의 방식은 메모리 낭비와 효율성이 떨어지게 됩니다.
싱글톤 패턴을 사용하여 위와 같은 문제를 해결할 수 있습니다.
싱글톤 패턴 적용 코드
먼저 sample_Project 패키지에 singleton 패키지를 생성합니다.
이후 싱글톤 패키지에 SingletonService.java 파일을 생성하고, 아래의 코드를 작성합니다.
package developingman.sample_Project.singleton;
public class SingletonService {
// static 영역에 1개의 객체를 생성합니다.
private static final SingletonService instance = new SingletonService();
//객체 인스턴스가 필요하면 아래 public static 메서드를 통해서만 조회할 수 있도록 합니다.
public static SingletonService getInstance(){
return instance;
}
// 생성자를 private로 선언하여 외부에서 new 키워드를 통해 객체를 생성할 수 없도록 합니다.
private SingletonService() {}
}
일전에 작성하였던 SingletonTest.java 클래스를 수정합니다.
package developingman.sample_Project;
import developingman.sample_Project.singleton.SingletonService;
public class SingletonTest {
static SingletonService singletonService1 = SingletonService.getInstance();
static SingletonService singletonService2 = SingletonService.getInstance();
public static void main(String[] args){
System.out.println("memberService1 : " + singletonService1);
System.out.println("memberService2 : " + singletonService2);
}
}
아래와 같은 출력결과를 확인합니다.
위와 같이 같은 SingletonService를 사용하는 모든 객체의 주소가 같아짐을 확인할 수 있습니다.
스프링을 이용한 싱글톤 방법
스프링 컨테이너의 기본값(default)은 싱글톤입니다.
싱글톤 패턴의 문제점
- 싱글톤 패턴을 구현하는 코드 자체가 많습니다.
- 의존관계상 클라이언트가 구체 클래스에 의존합니다.
- 지정해서 가져오기 때문에 테스트하기 어렵습니다.
- private 생성자를 사용하여 자식 클래스를 만들기 어렵기 때문에 유연성이 떨어집니다
- 속성공유
- 멀티쓰레드 환경에서 싱글톤 객체의 속성은 여러 쓰레드에 의해 바뀔 수 있습니다.
- A 쓰레드에선 속성 값을 x로 바꾸고 출력하는 과정에서 B 쓰레드가 속성 값을 y로 바꾸면 쓰레드 A에선 예상하지 못한 값이 나올 수 있습니다. (1개의 인스턴스에서 속성 값을 공유하기 때문에 발생하는 문제점입니다.)
- 가급적 읽기만 가능해야 합니다.
- Application 초기 구동 시 인스턴스 생성
- 싱글톤 빈은 기본적으로 애플리케이션 구동 시 생성되므로 싱글톤 빈이 많을수록 구동 시간이 증가할 수 있습니다.
싱글톤 패턴 문제의 해결
- 싱글톤 패턴문제를 싱글톤 컨테이너가 해결해 줍니다.
- 스프링 컨테이너는 싱글톤 컨테이너 역할을 합니다.
- 싱글톤 객체로 생성하고 관리하는 기능을 싱글톤 레지스트리라고 합니다.
- 스프링 컨테이너의 기능 덕분에 싱글턴 패턴의 모든 단점을 해결하여 객체를 싱글톤으로 유지할 수 있습니다.
dependencyConfig.java 클래스를 수정합니다.
package developingman.sample_Project;
import developingman.sample_Project.member.MemberRepository;
import developingman.sample_Project.member.MemberService;
import developingman.sample_Project.snack.SnackRepository;
import developingman.sample_Project.snack.SnackService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DependencyConfig {
@Bean
public MemberService memberService() {
return new MemberService(new MemberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemberRepository();
}
@Bean
public SnackService snackService(){
return new SnackService(new SnackRepository());
}
@Bean
public SnackRepository snackRepository() {
return new SnackRepository();
}
}
- @Configuration과 @Bean 애너테이션을 추가합니다.
- @Bean을 통해 스플이 컨테이너에 등록됩니다.
SingletonTest.java클래스를 수정하여 싱글톤 컨테이너 테스트 코드를 추가하겠습니다.
package developingman.sample_Project;
import developingman.sample_Project.member.MemberService;
import developingman.sample_Project.singleton.SingletonService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SingletonTest {
static AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(DependencyConfig.class);
static MemberService memberService1 = ac.getBean("memberService", MemberService.class);
static MemberService memberService2 = ac.getBean("memberService", MemberService.class);
public static void main(String[] args){
System.out.println("memberService1 : " + memberService1);
System.out.println("memberService2 : " + memberService2);
}
}
코드를 수정한 후 아래와 같은 결과를 확인합니다.
'Spring' 카테고리의 다른 글
[Spring] 다양한 의존 관계 주입 방법 (0) | 2023.04.09 |
---|---|
[Spring] 컴포넌트 스캔(Component Scan) (0) | 2023.04.07 |
[Spring] 빈 스코프(Bean Scope) (0) | 2023.04.05 |
[Spring] 빈(Bean) (0) | 2023.04.04 |
[Spring] 스프링 컨테이너(Spring Container) (0) | 2023.04.03 |