스프링이 생성과 제어를 담당하는 오브젝트로, 다른 설정을 부여하지 않는다면 기본적으로 singleton scope를 갖는다.
ApplicationContext ctx =
new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao = ctx.getBean("UserDao", UserDao.class);
UserDao dao2 = ctx.getBean("UserDao", UserDao.class);
System.out.println(dao);
System.out.println(dao2);
위의 코드를 실행했을 때, 출력되는 결과는 dao를 출력했을 때와 dao2를 출력한 결과가 서로 같다. 즉, getBean을 통해 서로 같은 bean을 공급받는다.
이처럼 스프링은 기본적으로 bean 오브젝트들을 singleton으로 만든다.
스프링이 적용되는 대상이 서버 환경이기 때문이다. 서버로 들어오는 하나의 요청을 처리하기 위해서는 비즈니스 로직, 서비스 로직 등 각 로직 단에서 필요한 수많은 오브젝트들이 존재한다. 하나의 요청에도 많은 오브젝트들을 필요로 하는데, 요청이 조금만 더 많아진다면 서버에서 처리해야 할 오브젝트들의 수는 기하급수적으로 증가하게 된다. 따라서 하나의 오브젝트만 만들어두고 여러 thread에서 공유해 동시에 사용하여 서버의 부하를 줄인다.
Java Singleton 클래스는 classloader 단위이며, Spring Singleton 클래스는 Application context 단위이다.
Tomcat은 위와 같이 classloader를 계층적으로 관리한다. 각 Web Application(Webapp1, Webapp2, ...)마다 classloader를 따로 사용하므로 서로 다른 war 파일에 속한 클래스 간에는 서로 참조할 수 없다. 동일한 class를 사용한다 하더라도 static 변수를 공유하지 않는다. 물론, Common에 static으로 선언될 경우에는 동일한 인스턴스를 참조할 수 있다.
Spring Singleton은 WebApplicationContext(Spring IoC Container)안에서 하나만 생성되고 공유된다. 따라서 서로 다른 servlet context들 끼리는 참조가 불가능하다. 하지만 이 상황에서 Java Singleton 클래스의 static 변수는 서로 참조가 가능하다.
servlet1, servlet2에서 SharedBean을 각각 Singleton Bean으로 등록하면 서로 다른 인스턴스를 생성하지만 SharedBean의 static 멤버변수는 servlet1과 servlet2가 동일한 인스턴스를 참조한다.
다음은 가장 보편적으로 사용되는 싱글톤 구현법이다. static으로 선언된 객체는 메모리 할당이 한 번만 이루어지고, final로 선언되면 그 값을 덮어쓸 수 없음을 이용하였다.
public class MyConnection {
private MyConnection() { }
private static class MyConnectionHolder {
static final MyConnection CONNECTION = new MyConnection();
}
public static MyConnection getConnection() {
return MyConnectionHolder.CONNECTION;
}
}
[참조] 토비의 스프링 3.1
[참조] https://judekim.tistory.com/91
[참조] https://enterkey.tistory.com/300