정말 궁금했었고,
항상 찾아봐도 제가 원하는 그림 중
일부 단면만 찾아볼 수 있던 내용에 대해 정리할 수 있던 하루였습니다.
이 글을 읽고 나시면,
서블릿 컨테이너와 스프링 컨테이너의 관계
WAS와 WS의 관계에 대해 좀 더 깊이있게 이해할 수 있을 것이라 생각합니다.
이 설명은, 마치 아래의 그림과 같다할 수 있습니다.
몇몇 블로그를 찾아보면, 이와 같은 그림의 설명들이 있습니다.
서블릿 컨테이너가 스프링 컨테이너를 포함한다는 것인데, 호출 순서 측면에서 보면 납득할 수 있지만, 이는 잘못된 표현입니다.
회사에 기획 팀, 프론트앤드 팀, 백앤드 팀이 있다면 기획 팀 안에 프론트앤드 팀이 있는 느낌입니다.
기획 팀, 프론트앤드 팀, 백앤드 팀 간에 긴밀하게 상호 협조하여 돌아간다는 표현이 적절할 것입니다.
이와 마찬가지로, 서블릿 컨테이너와 스프링 컨테이너는 긴밀한 서로간의 협조를 통해 웹 어플리케이션을 구동해준다 표현할 수 있겠습니다.
Apache는 깃털 모양이고, Tomcat은 고양이입니다.
즉, 원래 두 서비스는 독립된 서비스였습니다.
Apache
는 웹서버의 역할을, 톰캣
은 어플리케이션 서버의 역할을 했습니다.
웹 어플리케이션 서버를 구동하기 위해
예전에 설치해야하는 서비스는 Apache, Tomcat, 이 둘을 연결해주는 모듈. 이렇게 3가지 요소를 설치했어야 했습니다.
이후 Apache
와 Tomcat
이 합쳐졌고, 이것이 Apache Tomcat
이라고 합쳐졌습니다.
Tomcat
을 Application Server
라고도 하고, Servlet Container
라고도 합니다. 뉘앙스로 같은 대상을 가리키는 것이라 할 수 있겠습니다.
웹서버(Apache)가 앞단에 있어서, 정적 리소스를 돌려주고. 동적인 처리를 요구하는 경우, 톰캣(Servlet Container)이 관리하는 서블릿을 호출하여 처리해주었습니다.
즉, 이때는 스프링 ( 스프링 컨테이너 )의 도움 없이, 웹 서비스를 자바 자체만으로 제공하는 구조였습니다.
톰캣이 처음 구동될 때, server.xml
을 통해 서버 자체의 설정을 마쳐준 뒤,
톰캣 자체의 web.xml
로 톰캣이 관리하는 서비스들의 공통 설정을 진행합니다.
우리 웹 어플리케이션의 /WEB-INF
의 web.xml
을 통해 톰캣 자체의 웹 공통 설정을 제외한 우리 어플리케이션 자체의 웹 관련 설정을 해줍니다. 예를 들어, 특정 서블릿을 주어진 URI를 매핑해줍니다.
여기서 초록색으로 표현된 DS
는, Spring의 DispatcherServlet으로 서블릿 컨테이너에서 스프링 컨테이너로 들어가는 진입점 역할을 합니다.
request 마다 Application Server에서 thread가 생성됩니다. ( Tomcat이 자체적으로 Thread Pool을 관리합니다 )
Tomcat ( = Application Server, Servlet Container )가 하는 일
=> JSP -> Servlet ( .class 파일로 컴파일 )
=> 요청 시, 서블릿 초기화 ( init() + service() ) 호출, 이후 요청에 대해 service() 호출
동적 생성, 단일 프로세스에 사용자 요청마다 멀티 쓰레드로 서로가 블록킹, 동기화 제어 없이
각자가 빠르게 답변을 받아갈 수 있는 형태로 실행됨.
정적인 자원을 요구하는 요청을 처리하냐, 동적인 요청을 처리하는 차이가 있습니다.
멀티 프로세스와 단일 프로세스 - 멀티 쓰레드의 차이입니다.
Apache Web Server의 경우, 요청 당 프로세스
가 하나 생성되고
Tomcat
Application Server에서 해당 웹 서버의 프로세스에 대한 요청에서 Thread
로 받습니다.
어플리케이션 서버는 메모리의 힙 영역을 참조해서, 힙 영역에 인스턴스가 생성되어 있지 않다면 힙 영역에 인스턴스가 생성되어 있으면 만들어줍니다. 들어온 요청마다 Thread가 동작해서 나눠 사용합니다.
최초에 부른 애가 서블릿 메서드 - init()
을 해준다.
JSP
를 Servlet
으로 변환해서 메모리에 올려놓는 작업다시 복습하면서, Tomcat의 설정부터 DispatcherServlet, Spring Container의 초기화까지 어떻게 진행될 수 있는지 정리해보겠습나다.
개인적으로 매우 크게 정리가 되어 좋았던 내용입니다.
서블릿의 초기화
라 할 수 있습니다.servlet-context.xml
설정을 받아 DispatcherServlet에 초기화할 설정 정보를 전달합니다. => 이 순간에 Spring Container가 만들어지고 초기화됩니다.Servlet Container
는 javax
의 것이고, Spring Container
는 Spring
의 것이라는 것이 차이점입니다.
즉, 해당하는 라이브러리와 관리 체계가 다르고, 사용하는 방법 또한 다릅니다.
VM에서 참조하는 라이브러리와 영역 관리가 다른 것입니다.
그렇지만, 상호간 긴밀한 협업을 통해 웹 어플리케이션 서비스를 효율적으로 구동할 수 있도록 해줍니다. ( 이 둘을 하나의 톰캣 안에 꾸겨넣는다 표현할 수 있습니다. )
DI ( Dependency Injection )
서블릿 컨테이너가 만들지 않는 그 외 모든 인스턴스 ( 서블릿 제외 모든 POJO )는 스프링 컨테이너가 관리합니다.
쉽게 말해서, Spring Container가 Servlet을 제외한 VO, DAO를 new를 통해 생성해주고 관리해줍니다. ( 사실은 내부적으로 Reflection API를 사용하므로 new를 사용한다는 표현은 잘못되었다 생각합니다 )
Spring에서 만들어 중앙 집중형으로 관리하는 스프링 빈을 ApplicationContext 라는 매개체를 통해 자신 내부 혹은 외부 서블릿 (톰캣)에게 전달해줍니다.
스프링 컨테이너는 서블릿 컨테이너가 만들어 관리하지 않는 모든 인스턴스를 생성해주고, 관리하고 제공해줍니다.
스프링에서 관리하는 클래스들을 스프링 빈이라 하고, 스프링 컨테이너가 관리해줍니다.
스프링을 쓰겠다는 말은, 스프링 컨테이너를 통해 우리 빈을 관리하겠다는 말과 동일합니다. 즉, 우리가 쓸 모든 클래스, 인스턴스, 그들간의 관계를 Spring이 관리해주도록 하는 것입니다.
기존에 GC에 의존한 인스턴스 관계를, 스프링 컨테이너가 빈의 생명주기 ( 라이프사이클 )을 관리하도록 해줄 수 있습니다.
이런 인스턴스를 필요로 하는 어떤 다른 클래스에게 제공해주는 것을 의존성 주입
이라 합니다.
의존성을 준다는 것은, Bean 이라는 것을 만들어서 ( = 관리 대상의 클래스를 만들어서 ), Bean의 인스턴스를 만들어 Servlet Container ( = Tomcat )에게 주는 것입니다. 이것이 Spring Container가 해야할 일입니다. ( 의존성을 제공해주는 역할 )
인스턴스를 언제 만들고, 누구에게 제공할 것인지, 어떠한 방식으로 생성하고 관리할 것인지가 Spring Container가 해야할 가장 큰 일이라 할 수 있습니다.
서블릿은 스프링 컨테이너의 관리 대상이 아닙니다, 대신 Controller라는 POJO를 등록하여 DispatcherServlet에게 위임받은 웹 요청을 라우팅하여 이를 처리할 수 있도록 합니다.
이렇게, 웹 요청을 처리하는 주체인 서블릿 컨테이너 (톰캣)에 대해 톰캣이 필요로 하지만, 서블릿이 아닌 POJO 클래스들(= 스프링 빈)을 하나하나 생성하고 관리하다 필요할 때 꽂아주는 역할을 하는 것이 스프링 컨테이너라 할 수 있겠습니다.
이런 스프링 빈은 결과적으로 JVM위의 Heap 메모리 영역에 생성되기에, 사용 대상이 서블릿이던, 다른 스프링 컨테이너의 스프링 빈이던 모두 참조하여 사용할 수 있습니다.
스프링에서는 기본적으로 어플리케이션 생성-종료시 까지 필요한 인스턴스가 하나씩만 유지되는 singleton 방식으로 빈을 생성합니다만, 빈을 요청할 때마다 생성해주는 prototype scope, 웹 요청시마다 생성되는 request scope, HttpSession 유지 시간까지 유지되는 session scope, 톰캣 위에 우리 어플리케이션 레벨에서 유지되는 application scope, 톰캣 자체가 shutdown 될 때 까지 유지되는 global session scope로 생성될 수 있습니다.
XML을 읽어서 무엇을 하는지?
ContextLoaderListener
에게 일을 시킨다. = 이 친구가 ApplicationContext를 로드합니다
각자 Context와 관련한 일 = servlet-context.xml ( = DispatcherServlet )
DispatcherServlet이 ApplicationContext에게 일을 시킵니다.
root-context.xml
에게 시키는 일? = Tomcat이 여러 개의 웹 어플리케이션 사이의 공통 초기화 작업을 수행해줍니다.
학급이 생성되면 반장이 생기는 것 처럼, DispatcherServlet이 생성되는 순간 servlet-context.xml을 통해 ApplicationContext가 생성됩니다 ( 정확히는 Web 환경이기에, WebApplicationContext 가 생성됩니다. 이 때, ViewResovler또한 초기화되어 빈으로 매핑됩니다.
ApplicationContext에 Web관련 기능을 추가한 것이 WebApplicationContext입니다.
Controller, ViewResolver, LocaleResolver, HandlerMapping 등과 같은 웹 관련 스프링 빈들을 관리해줍니다.
여러 개의 WebApplicationContext가 있는 것은, 톰캣 위에 여러 개의 웹 어플리케이션이 떠있을 수 있기 때문입니다.
BeanFactory
빈 객체 관리 및 빈 객체간 의존관계 설정 기능을 제공합니다.
주된 구현체 : XmlBeanFactory
new 해서 만들어주는 기능만을 합니다 (초간단)
인스턴스를 만들어서 가져가는 역할을 합니다.
ApplicationContext
src/main/resource
읽어들이기 ( ex. log4j.xml
)/WEB-INF/spring
내의 파일 등, C:\IT\Installation
.. 등의 src/main/resource
외부의 파일 시스템src/main/resources
와 src/main/webapp/resources
는 각각 자바 어플리케이션 자체의 자원 vs 웹 어플리케이션에서의 자원의 차이이다.WebApplicationContext
Web Application
당 하나씩 생성되고, 웹 관련 컴포넌트들의 의존성을 관리해줍니다.XmlWebApplicationContext
말 그대로, Spring Framework에서 생명주기가 관리되는 클래스들입니다. ( POJO 형태입니다 )
종류로는 DAO, DataSource, Transaction Manager, Persistence Managers, Service 등이 있습니다.
Bean은 생성 방식에 따라 싱글톤, 프로토타입 .. 자동 의존관계 주입을 지원하며, Spring Container에게 초기화 시 발동되는 메서드, 소멸되기 전에 발동되는 메서드 등을 가집니다.