일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- 의존성주입
- 원티트 프리온보딩
- 벨로그
- 웹크롤링
- 도커
- 수정자주입
- 프리온보딩 4월 백엔드 챌린지
- commit message
- Spring
- 원티드 챌린지
- 인텔리제이
- 자바
- velog
- Java
- 필드주입
- 의존성자동주입
- 파이썬
- 스파프타 코딩클럽
- 알고리즘
- 스파르타 코딩클럽
- docker
- 자료구조
- 개발자 커리어콘
- 스프링
- 원티드 백엔드 챌린지
- 원티드프리온보딩백엔드챌린지
- 커밋메세지
- 생성자주입
- springboot
- 캐치
- Today
- Total
기록하는 블로그
[Spring] 스프링 파헤쳐보기 - Listener ~ DispatcherServlet 본문
스프링을 접한 지 어느덧 9개월 정도 지났다. 그동안 스프링만 공부한 것이 아니라 SQL, JS 등등 여러 가지를 공부했기 때문에 스프링 자체에 대한 깊이 있는 지식을 갖고 있다고 자부하기는 좀 어려웠다. 그래서인지 스프링을 처음 접했을 때부터 뜬구름 잡는 요소들이 몇 가지 있어서 그것들을 확실하게 짚고 가기로 했다.
이 그림은 학원다녔을때 강사님이 그려주신 그림인데 왜 그때 확실하게 개념을 잡지 않았을까 하는 후회가 새삼 든다. 하지만 그때는 그날 진도 따라가는 것부터 혼돈이었으니까 그런 거라고 생각하자
사실 Spring MVC를 배울때 썼던 자료 기는 하지만 SpringBoot도 어차피 기반으로 하는 것은 비슷할 테니 이 순서대로 공부해 보기로 했다.
Listener (ContextLoaderListener)
이상하게 다른 객체들은 잘 나왔는데 리스너만 잘 나오지 않았다. 찾아보니 리스너는 리스너라고 정의되어있는 것이 아니라, 이벤트가 생겼을 때 이것에 대응하기 위해 이벤트 핸들링을 하는 객체를 지칭하는 것이었다. 스프링에서 이 리스너는 스프링의 조상인 서블릿의 ServletContextListener를 상속한 ContextLoaderListener로 구현되어있다. 그리고 스프링에서 이 ContextLoaderListener은 애플리케이션의 시작과 종료 시 발생하는 이벤트를 핸들링하는 리스너 역할을 한다.
package javax.servlet;
import java.util.EventListener;
/**
* 이 인터페이스를 구현하면 웹앱의 일부인 서블릿 컨텍스트의 변화에 대한 알림을 받음
* 알림을 받으려면 구현한 클래스는 반드시 배치 스크립터에 명시해줘야함
*/
public interface ServletContextListener extends EventListener {
/**
** 웹앱이 초기화되는 것을 알려줌
** 웹 애플리케이션의 필터 또는 서블릿이 초기화되기 전에 모든 ServletContextListener에게
** 컨텍스트 초기화 알림이 전송.
*/
public void contextInitialized ( ServletContextEvent sce );
/**
** 서블릿 컨텍스트가 종료 될 것이라는 알림.
** 모든 서블릿과 필터는 ServletContextListeners가 컨텍스트의 종료알림을 받기전에 파괴됨
*/
public void contextDestroyed ( ServletContextEvent sce );
}
ServletContextListener는 이렇게 생겼다. 대충 훑어봐도 웹앱의 시작과 끝을 알려주는 것이라는 것을 알 수 있었다.
그렇다면 이 ServletContextListener를 상속한 ContextLoaderListener 구현체는 어디 있는 것일까? 최근에 스프링 부트를 사용하느라 작성할 일이 없어서 찾기 힘들었는데 이전에 만든 프로젝트를 찾아보면서 그 해답을 찾다가 스프링 웹 MVC의 동작 순서에 대해 다시 파봤다.
스프링 기반의 웹앱을 시작하면 하나의 ServletContext가 생성되는데 이곳에는 여러 서블릿들이 공유하는 공유자원들을 가지고 있다. 그리고 이 ServletContext안에는 두 가지가 있는데,
ContextLoaderListener에 의해서 만들어지는 Root WebApplicationContext
- ContextLoaderListener는 WebApplicationContext에 여러 자바빈들을 적재하는 역할을 한다.(root) ex ) Service 클래스, Repository 클래스, Transaction Manager
- 별다른 파일의 지정이 되어있지 않으면, applicationContext.xml 안에 있는 자바빈들을 적재한다.
- WebApplication 전체에 사용 가능한 DB 연결, 로깅 기능들이 이용된다.
DispatcherServlet에 의해서 만들어지는 Child WebApplicationContext
- 직접 사용하는 컨트롤러를 포함한 웹 관련 빈을 등록하는 데 사용한다. ex ) Web Layer 클래스, Contoller, ViewResolver 등등
- 별다른 파일의 지정이 되어있지 않으면, servlet-context.xml 안에 있는 자바빈들을 적재한다.
- Child WebApplicationContext은 Root WebApplicationContext의 자바빈들에 접근이 가능하다.
- 각각의 Child WebApplicationContext 끼리의 빈 공유는 불가능하다.
- ContextLoaderListener를 이용하여 여러 DispatcherServlet의 설정 파일을 한 번에 로드하는 방식 사용
몇 가지 특징들을 좀 정리해보면
- WAS에서 서블릿 컨텍스트(웹 서비스)가 시작될 때 스프링관련 빈들을 Root WebApplicationContext에 적재하고 서블릿 컨텍스트가 종료될 때 빈들을 제거한다.
- Spring의 Root WebApplicationContext의 시작과 종료를 위한 리스너이며 생성되는 Root WebApplicationContext는 ServletContext에 저장된다.
- 설정 파일은 contextConfigLocation 속성에서 설정하지 않으면 WEB-INF/applicationContext.xml이다.
- ContextLoaderListener와 DispatcherServlet 은 각각 WebApplicationContext 중 Root가 먼저 빈들을 로드한다.
- ContextLoaderListener는 영속성 계층의 클래스(DAO 클래스, @Repository), 서비스 계층의 클래스(@Service), 엔티티(@Entity)와 같은 클래스들을 주로 로드한다.
- DataSource, MyBatis를 위한 SeqSesionFactoryBean, TransactionManager 같은 클래스들을 컨트롤러의 실행 전에 메모리에 생성되어 있어야 하므로 DispatcherServlet이 컨트롤러 클래스(@Controller)를 로드하기 전에 ContextLoaderListener를 통해 로드해야 한다.
web.xml 에는 다음과 같이 설정한다.
리스너가 자바 빈들을 로드하기 위해서 참조해야 하는 xml 파일의 위치는 context-param에 정의한다.
생략하면 자동으로 applicationContext.xml에서 찾는다.
두 개의 WebApplicationContext 중 Child는 DispatcherServlet에 의해서 생긴다고 하기도 했고 그림에서도 리스너에서 DispatcherServlet로 넘어가서 이 부분에 대해서 알아봤다.
DispatcherServlet
- HttpServlet을 상속받은 서블릿이고 web.xml에서 정의
HttpServlet은 사용자의 요청을 처리하는 메서드로 doGet, doPost를 가지고 있고 각각
HttpServletRequest와 HttpServletResponse 객체를 매개변수로 가지고 있다. 이 두 객체는 서블릿과 클라이언트 사이를 연결해주는 중요한 객체이다.
- 사용자의 요청을 맨 앞단에서 받아 HandlerMapping 빈에서 URI와 요청정보를 넘겨 이 요청에 맞는 컨트롤러로 요청을 위임한다.
- 컨트롤러에서 비즈니스 로직이 처리된 후
- 컨트롤러로부터 Model과 View를 리턴
- 리턴 결과는 ViewResolver가 View 이름을 해석하여 요청을 처리할 View로 요청을 Forward(ex - JSP 등등)
- 이후 View가 처리한 결과를 받아서 클라이언트 브라우저에 전달
- DispatcherServlet이 로딩되면 아래 코드에 있는 contextConfigLocation에서 파라미터로 특정 파일을 지정하거나 그렇지 않다면 WEB-INF/서블릿 이름-servlet.xml 파일에 정의된 빈들을 WebApplicationContext에 로딩한다. 그런 다음 servlet-mapping을 통해 다루어질 URL을 지정한다.
이 내용을 보고 내가 예전에 한 방식과 인터넷을 찾아보니 이클립스에서 자동으로 생성해줬던 servlet-context.xml 에서 빈들의 정보를 가지고 왔던 것 같다. 또 하나의 자동으로 생성되었던 파일 applicationContext.xml 은 Root Web Application Context가 정보를 가지고 올 때 썼던 파일이다.
- DispatcherServlet의 설정 파일에는 Controller, ViewResolver, LocaleResolver, MVC 기반 인프라와 관련된 빈들을 주로 정의한다.
- 세 개의 파라미터를 가진다.
- contextConfigLocation : 설정파일 위치
- namespace : WebApplicationContext의 네임스페이스, 기본은 [서블릿명]-servlet.xml 형식이다.
- contextClass : WebApplicationContext를 구현할 클래스, 기본은 XmlWebApplicationContext 이다.
- load on startup 설정이 없으면 사용자의 요청을 받을 때 DispatcherServlet을 생성함으로 일반적으로 서블릿 생성 시 같이 생성하는 옵션을 둔다.
XML에서 DispatcherServlet을 정의하는 예시 코드 (web.xml에 자동으로 생성됨)
웹 애플리케이션이 동작할 때의 순서를 정리해보자면
- 서블릿 컨텍스트 생성하고 WAS 가 web.xml을 배치
- 컨텍스트 초기화 이후 <listener>에 정의되어있는 ContextLoadListener를 메모리에 적재
- ContextLoadListener를 정의해놓은 파일을 찾아서 그 안에 있는 자바빈들을 적재
- DispathcherServlet 생성하고 사용자의 요청을 기다림
배울 때는 이 정도의 깊이로 배우지 않았는데 오히려 혼자서 공부하면서 파고 또 파니 더 많이 공부하게 되는 것 같다.
Model과 ViewResolver 등등은 다음 포스팅 때!
'Spring' 카테고리의 다른 글
[Spring] 스프링 파헤쳐보기 - ViewResolever, Model (0) | 2021.05.27 |
---|---|
[SpringBoot] 어노테이션 정리 (1) (0) | 2021.02.24 |