본문 바로가기
Spring/개념

Servlet과 Spring Container

by clearinging 2021. 12. 4.
반응형

Servlet과 Spring Container

Servlet

정의

  • request 요청을 처리해주는 (비즈니스 로직을 실행)

구성

  • init() : servlet 객체를 생성할 때 사용하는 메서드
  • service() : servlet의 요청에 대해서 비즈니스 로직을 실행하는 단계
  • destory() : servlet 종료/삭제 전 실행하는 비즈니스 로직

특징

  • servlet은 HttpServlet으로 상속을 받아서 사용합니다.
  • 상속 받은 객체에서는 doPost, doGet 등 함수로 변경해서 사용
  • 우리가 사용하는 tomcat인 경우에는 servlet 객체를 사용 후 바로 삭제 하지 않고, 싱글톤으로 사용함 -> reload나, tomcat이 끝나기 전에 destory 작업 진행
  • spring framework 에서는 front controller 구조를 가지기 때문에 하나의 Servlet만 사용함(Dispatcher servlet이라고 하며 나중에 설명함)

Servlet Container

  • servlet container는 servlet객를 생성 실행 소멸을 관리해주는 container 입니다.
  • Spring 에 Spring에서 사용하는 ServletContainer랑 햇갈리 수 있지만 다른 것입니다.
    • spring의 ServletContainer는 Spring package에 존재하면 Spring Bean life cycle을 관장 하는 부분 입니다.
    • tomcat에 있는 Servlet Container는 org.apache.catalina에 interface로 선언되어 있습니다.
    • 결론 완전 다른 겁니다.
  • Servlet Container는 설정 파일을 참고해(web.xml) mapping할 servlet class를 찾고, 그 로직을 실행합니다.
  • servlet 객체를 생성하는데 많은 시간이 소요되므로 주로 singleton으로 미리 생성해서 재사용하는 형태를 사용합니다
  • Spring 에서는 web.xml에 정의한 dispatcher servlet하나로만 처리 -> front contrller pattern

Servlet Container의 요청 처리 순서

  • 처음에 Servlet Request/Response 객체 생성
  • 설정 파일(xml)를 참고해서 mapping할 servlet확인
  • servlet 객체가 존재 유무에 따라 생서 혹은 재사용 함
  • servlet container는 thread 생성해서 request, response 인자를 service 인자로 전달해서 비즈니스 로직 처리
    • request 에 대한 Thread에 별도로 새로운 Thread를 생성합니다 -> 오버헤드 발생
  • 처리 완료되면 thread 종료 -> http request / response 소멸

Spring Context

ServletContext

  1. 정의
  • 하나의 servlet(Spring에서는 DispatcherServlet)과 servlet container와 통신하기 위해서 사용되어지는 method들이 존재는 class
  • Servlet당 1개의 ServletContext를가지게 됩니다
  • Application공통 자원이나 정보를 미리 바인딩 해서 서블릿들이 공유하여 사용할 수 있게 해주는 것입니다
  1. 역할
  • 다른 Servlet의 정보를 가져오는 것이 가능(servlet cotainer랑 상호 작용하기 때문)
  • Servlet에서 request body에 있는 file에 접근가능
  • file의 MIME type을 얻는 일 -> getMineType()
  • log file에 기록 가능 -> log()
  • servlet자체의 정보를 얻어내는 일 -> getServletInfo()
  • servlet의 가상디렉토리상의 실제 경로를 얻는 일 -> getRealPath()
  • servlet의 버전 확인 -> getMajorVersion(), getMinorVersion()

ApplicationContext

  1. 정의
  • Spring IoC를 담당하는 Bean라이프 사이클을 관리하는 객체입니다
  1. 특징
  • 계층 구조를 가지고 있으며, ServletContextListener를 상속받은 ContextLoaderListner를 통해서 생성이 됩니다(아래 내용 참고)
    • RootApplicatonContext
    • ServletApplicationContext

  1. RootApplicationContext
  • ComponentScan, @Bean으로 생성된 모든 객체들이 해당 됩니다
  • ServletApplicationContext에 접근을 못합니다
  • ServletContext에서 생성한 ApplicationContext가 RootApplicationContext 입니다
  1. ServletApplicationContext
  • ServletContext와 햇갈릴 수 있지만 다른 존재합니다
  • ServletApplicationContext에서 Bean객체 조회 후 존재하지 않을 경우 RootApplicationContext에서 조회를 진행 합니다
  • DispatcherServlet이 직접 생성하는 ApplicationContext 입니다 그러므로 생성한 DispatcherServlet내부에서만 사용가능
  1. 계층구조를 설정한 이유
  • DispatcherServlet이 여러개 존재할 수 있습니다
  • 개개인의 DispatcherServlet가 필요한 bean들은 ServletApplicationContext에 정의를 합니다
  • HandlerMapping, ViewResolver ...etc를 의미합니다
  • 공용으로 사용할 수 있는 것은 Root ApplicationContext에 등록 하면 됩니다
  • web.xml에 계층 구조를 설정하지 않고 @Bean과 ComponentScan으로 Bean객체를 등록하면 아래와 같이 Bean이 Root에만 등록이 됩니다

ContextLoaderListner

  • ServletContextListener에서 구현하고, ContextLoader를 상속받은 객체 입니다
  • ServletContextListener는 단지 Context의 life cycle을 감지(옵저버 패턴)하는 역할을 합니다
    • Servlet 생성과 삭제 단계에서 추가 로직을 실행하는 역할을 합니다
  • ContextLoader는 실제 ServletContextListener에서 사용할 로직이 들어가 있습니다
  • Servlet생성 시점에 Root ApplicationContext 생성하고 ServletContext에 Root ApplicationContext을 등록합니다
  • servlet삭제 시점에 Root ApplicationContext를 ServletContext에서 제거 합니다
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext()); 
        // RootApplicationContext생성
        // ServletContext에 RootApplicationContext등록
    }

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext()); // 할당 해제
    }
}

Front Controller 디자인 패턴

정의

  • 모든 요청은 단일 controller를 통해서 처리 되고, 적절한 handler에 매칭 된다.
    • handler에 대한 설명은 Spring Container 설명할 때 다시 언급하겠습니다.

이유

  • 관리의 측면
    • 멀티 스트레드를 관리해야함
    • 멀티 스레드의 context switching 비용 또한 많은 비용이 들게 됨
      • 요청이 들어온 후 Servlet을 생성하거나, 기존에 Servlet에 매칭되게 되면 ContextSwitching해서 다른 Thread에 처리를 위임 하게 됩니다 그사이에 ContextSwitching이라는 overhead가 발생
  • 개발 측면
    • 각 url 마다 동일한 요청 처리 로직(handler에 mapping시켜 주는 로직)을 설정하다 보니 코드의 중복성이 증가
    • 기존에 spring mvc가 없을 경우 url 당 servlet을 하나씩 다 정의해서 처리해야 하기 때문에 xml 파일 관리도 많은 에너지를 소모 하게 되었었습니다.

Front Controller 이름

  • Dispatcher Servlet

생성

  • AbstractAnnotationConfigDispatcherServletInitializer(과거의 web.xml)을 상속 받은 WebInitializer class가 Disatcher Servlet를 생성
  • Dispatcher Servlet은 WebApplicationContext를 생성하고, 소유하게 됩니다.
  • @EnableWebMvc: MVC 관련된 Annotation을 지원하고, 스프링의 타입 변환과 formatting 시스템을 등록하는 기능을 제공합니다. 현재 Spring Boot 에서는 기본적으로 지원하기 때문에 굳이 선언할 필요는 없습니다.
  • DispatcherServlet은 뒤에 설명할 Handler Mapping, Handler Adapter, ViewResolver를 전부 Spring Container로부터 주입을 받아서 사용할 수 있게 해줍니다.
    • 그리고 이 Contianer를 우리가 Spring에서 존재하는 ServletContainer입니다(다른 블로그에서 WebApplicationContext 라고 부르기도 합니다) -> 자식 container

DispatcherServlet 구성

  • DispatcherServlet
  • ViewResolver, HandlerAdapter, HandlerMapping
    • Spring Container의 객체를 주입을 받아서 사용, 만약 등록이 되어 있지 않을경우 dispatcher servlet에서 제공하는 default로 설정합니다.
    • HandlerMapping: url 정보를 통해 어떤 Controller의 method를 호출하는 결정을 해줍니다
    • HandlerAdapter: handler mapping에서 설정된 controller를 호출해서 ModelAndView객체를 반환 해줍니다
      • RestController를 호출할 경우 View객체는 비어있습니다
    • ViewResolver: handler adapter로 받은 결과를 view(jsp/html) 형태로 변환

HandlerMapping

  • 해당 요청이 어느 controller에 전달할지 정해줍니다
  • 해당 객체의 어떤 method를 호출할지 정해줍니다
  • HandlerMapping을 등록하지 않아도 default로 dispatcher servlet이 등록 합니다
    • error message와 error page를 띄울때 사용합니다

HandlerInterceptor

  • dispatcher servlet이 HandlerAdapter를 통해서 handler(controller)를 실행하기전 후로 데이터를 가공 및 처리할때 사용 합니다

HandlerAdapater

  • HandlerMapping로 부터 얻어온 handler class/method정보를 통해서 실제 비즈니스 로직(handle method)을 호출 합니다
  • 그리고 ModelAndView객체로 반환 합니다
    • RestController인 경우 내부에 View객체는 없습니다

ModelAndView

  • client에게 반환할 데이터 정보를 담당하는 객체 입니다
  • RestController인 경우 View 객체가 없습니다
  • Controller인경우 html/jsp 파일 명정보와 내부에 넣을 데이터 model객체를 담은 객체 입니다

ViewResolver

  • ModelAndView에 정의된 html/jsp파일에 집어 넣어주는 역할을 담당합니다
  • 응답에 대한 rendering을 담당합니다
  • 아무 resolver를 등록하지 않을 경우 dispatcher servlet이 default ViewResolver를 등록합니다(InternalResourceViewResolver)
    • jsp를 지원하는 resolver 입니다

요청 처리 순서

  • servlet container는 Request, response 객체 생성해서 Dispatcher Servlet에 전달
  • Dispatcher Servlet은 Handler Mapping을 통해서 어떤 controller에 mapping 되어야하는지 찾음
    • Spring Framework에서 제공하고 있는 mapping 방식리스트 -> 방식은 web.xml에 등록을 해줘야 한다
      • BeanName 방식 : Bean이름과 url을 mapping 해줌 -> web.xml에서는 default로 설정
      • ControllerClassName 방식
      • SimpelUrl 방식
      • DefulatAnnotation 방식: Spring Boot에서 가장 익숙하게 사용하는 방식 url을 RequestMapping Annotation이 붙은 정보에 matching됨
  • HandlerMapping에서 찾은 Handler(Controller)의 method를 호출하 도록 HandlerAdapter가 호출 합니다.
  • controller 호출 결과를 HandlerAdapter가 받아서 ModelAndView 형태로 변경해서 Dispatcher Servlet에 전달
    • Model: controller처리 결과
    • View: 그 결과를 담는 하나의 페이지(Mvc에서는 String 형태로 전달 받는 다고 생각하면됩니다.)
  • DispatcherServlet은 실제 매칭된 View(타임리프, html) 가져오도록 View Resolver를 호출 (view name -> view 변경)
  • DispatcherSerlvet은 controller에서 전달받은 Model을 View에 심어줘서 사용자에게 반환

Spring Container

Spring Framework 실행할때 Bean과 Container 생성 순서

  1. web.xml로딩(현재는 AbstractAnnotationConfigDispatcherServletInitializer), ServletContext 생성
  2. ContextLoaderListner가 등록되어 있을 경우 ServletContext에 RootApplicationContext생성
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
    throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
    // 이미 root application context가 등록이 되어있을 경우 에러 발생
} else {
    // .. codes
    // Root Application Context 생성 -> ServletContext에 Root Application Context라는 곳으로 등록
}
  1. RootApplication에 등록할 Bean정보들을 load하고 DispatcherServlet생성
  2. Root ApplicationContext와 ServletApplicationContext에 Bean등록 후 ServletContex에 RootApplicationContext를 등록
  • RootApplicationContext에서는 ServletApplicationContext와 계층 구조를 가지도록 구현이 됩니다 -> 굳이 나누지 않으면 대부분의 Bean은 RootApplicationContext에 등록이 됩니다
  1. client 요청 입력
  2. DispatcherServlet에 파라미터 설정(Resolver, handlermapping ...etc)

  • dispatcher serlvet에 Bean class(HandlerMappging, HandlerAdapter, ViewResolver) 정보를 전달해줍니다.
  1. DispatcherServlet이 요청을 처리(HandlerMapping, HandlerAdapter, ViewResolver ...etc)

출처 : https://velog.io/@16616516/%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88

반응형

'Spring > 개념' 카테고리의 다른 글

AOP  (2) 2022.02.02
Logging  (0) 2021.12.17
Spring Test Mock 사용법 및 특징  (1) 2021.10.30
Spring Boot로 MySQL replication 연동하기  (0) 2021.10.06
04 Exception  (0) 2021.05.19