[Spring] @Component 어노테이션을 사용한 싱글톤패턴 유사 구현
프로젝트 개발 도중에 싱글톤 패턴을 사용하여, DB에서 데이터를 불러와 저장 후에 사용하고자 하였다.
그러나, 스프링 객체생성주기로 인해 싱글톤으로 생성한 인스턴스에서 DB 접근이 어려웠고, 공부하여 해결을 하고 싶었지만, 시간이 부족하여 유사한 방법으로 해결하고자 하였다.
Spring에서 제공하는 @Component 어노테이션을 사용하여 유사하게 구현해보았다.
1. @Component 어노테이션
- @Component 어노테이션을 붙인 클래스를 spring에서 스캔할 수 있도록 설정이 필요하다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
.....
<!-- 지정된 패키지 안에 있는 Bean 클래스들의 어노테이션을 분석하도록 지정한다 -->
<context:component-scan base-package="com.spring.test"/>
</beans>
- <context:component-scan> 태그의 base-package에 스캔하고자 하는 패키지 경로를 설정한다.
(해당 글에서는 예제를 위해 test 아래 모든 경로로 선언하였다.)
- 패키지가 여러개일 경우 위 태그를 여러개 작성하여 설정이 가능하다.
2. 구현
- 싱글톤으로 사용하고자 하는 클래스입니다.
@Component
public class singletonSample {
@Autowired
private SingletonService singletonService;
private List<Map<String, Object>> list = null;
public List<Map<String, Object>> getList() throws Exception{
if(this.list == null) {
System.out.println("새롭게 인스턴스 생성");
try {
// DB로 부터 데이터를 가져옴.
this.list = singletonService.getList();
} catch(Exception e) {
throw new Exception(e);
}
} else {
System.out.println("기존 인스턴스 생성되어 있음.");
}
// 생성한 list를 변경못하도록 선언
// 변경 시도시 RuntimeException이 발생한다.
return Collections.unmodifiableList(this.list);
}
}
- 해당 클래스를 @Component로 선언함으로써, Spring Bean에 등록하여 고정 메모리에 할당을 합니다.(싱글톤패턴)
- 다른 클래스에서 getList()를 호출할 때, list가 null 일 경우에는 DB를 통해 해당 List에 데이터를 할당합니다.
- 이후 다른 클래스에서 getList()를 호출할 때는 고정 메모리에 할당되어 있기때문에, 새로 생성하지 않고 기존 데이터를 사용하게 됩니다.
@Controller
public class TestController1 {
@Autowired
private singletonSample singletonSample;
@RequestMapping(value = "/test1", method = RequestMethod.GET)
public String home(Locale locale, Model model) throws Exception {
System.out.println(this.getClass().getSimpleName() + " 실행");
List<Map<String, Object>> singletonList = singletonSample.getList();
System.out.println(this.getClass().getSimpleName() + "에서 : " + singletonList);
return "test1";
}
}
@Controller
public class TestController2 {
@Autowired
private singletonSample singletonSample;
@RequestMapping(value = "/test2", method = RequestMethod.GET)
public String home(Locale locale, Model model) throws Exception {
System.out.println(this.getClass().getSimpleName() + " 실행");
List<Map<String, Object>> singletonList = singletonSample.getList();
System.out.println(this.getClass().getSimpleName() + "에서 : " + singletonList);
return "test1";
}
}
- 컨트롤러를 다음과 같이 간단하게 구현하여 확인을 해보겠습니다.
- @Autowired 어노테이션을 사용하여, singletonSample 클래스를 의존성을 주입합니다.
- 각각의 컨트롤러에서 singletonSample에서 getList()를 호출하고, 데이터를 전달받습니다.
- 프로젝트를 실행해서 두 개의 경로로 테스트를 해보면 결과는 아래와 같습니다.
TestController2 실행
새롭게 인스턴스 생성
TestController2에서 : [{gender=M, name=hwang, age=28, email=test1@naver.com}, {gender=W, name=kim, age=35, email=test2@naver.com}, {gender=M, name=Lee, age=24, email=test3@naver.com}, {gender=M, name=won, age=31, email=test4@naver.com}]
TestController1 실행
기존 인스턴스 생성되어 있음.
TestController1에서 : [{gender=M, name=hwang, age=28, email=test1@naver.com}, {gender=W, name=kim, age=35, email=test2@naver.com}, {gender=M, name=Lee, age=24, email=test3@naver.com}, {gender=M, name=won, age=31, email=test4@naver.com}]
참고