본문 바로가기
Spring/인프런 토비의 Spring Boot

섹션 4-7 [독립 실행형 스프링 애플리케이션] 스프링 컨테이너로 통합

by include_hoany 2024. 6. 1.

스프링 컨테이너로 통합

지금까지 만든 코드는 SpringContainer를 생성하고 Bean을 등록해서 초기화하는 작업을 해주는 SpringContainer작업 파트와 SpringContiner를 활용하면서 SubletContainer를 코드에서 생성하고 FrontController역할을 하는 DispatcherSublet을 등록하는 SubletContainer초기화 코드로 구분되어 질 수 있습니다.

이제는 이러한 작업을 하나로 통합하는 과정을 통해 SubletContainer를 만들고 Sublet을 초기화하는등의 작업을 SpringContiner가 초기화 되는 과정중에 일어나도록 코드를 수정해보도록 하겠습니다.

 

/*  
    Spring Container 구현, DispatcherServlet을 사용하기 위해서는
    GenericApplicationContext이 아닌 GenericWebApplicationContext 형식으로 생성해야합니다.
*/
GenericWebApplicationContext applicationContext = new GenericWebApplicationContext();

// Spring Container HelloController Bean등록 클래스의 구성정보 메타정보를 넘겨준다.  
applicationContext.registerBean(HelloController.class);

// Spring Container SimpleHelloService를 Bean등록 클래스의 구성정보 메타정보를 넘겨준다.  
applicationContext.registerBean(SimpleHelloService.class);

// ApplicationContext가 refresh메소드를 통해 빈 오브젝트를 생성합니다.  
applicationContext.refresh();

Spring Container의 초기화 작업은 Refresh라는 메소드에서 전부 일어납니다. refresh는 전형적인 템플릿 메소드로 만들어져있습니다. 템플릿 메소드 패턴을 사용하면 그 안에 여러 개의 Hook 메소드를 주입해 넣기도 합니다.

템플릿 메소드 안에서는 일정한 순서에 의해서 작업들이 호출이 되는데 그중에 서브 클래스에서 확장하는 방법을 통해서 특정 시점에 어떤 작업을 수행하게 해서 기능을 유연하게 확장하도록 만드는 기접입니다. 그 Hook메소드의 이름은  onRefresh입니다.

템플릿 메소드 패턴은 상속을 통해서 기능을 확장하도록 만든 부분이다보니 GenericWebApplicationContext라는 클래스를 상속해서 새로운 클래스를 하나 만들어야 합니다. 그래서 오버라이딩 해서 Hook 메소드의 기능을 집어넣어서 구현이 가능합니다.

다만 클래스를 따로 정의해서 구현하자니 번거로운면이 있어서 이번 통합 과정에서는 간단하게 익명 클래스를 활용하여 구현해보도록 하겠습니다.

 

기존 코드

package com.tobyspring.tobyspringboot;  
  
  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;  
import org.springframework.boot.web.server.WebServer;  
import org.springframework.boot.web.servlet.ServletContextInitializer;  
import org.springframework.web.context.support.GenericWebApplicationContext;  
import org.springframework.web.servlet.DispatcherServlet;  
  
public class TobyspringbootApplication {  
  
    public static void main(String[] args) {  
  
       /*  
          Spring Container 구현, DispatcherServlet을 사용하기 위해서는
          GenericApplicationContext이 아닌 GenericWebApplicationContext 형식으로 생성해야합니다.
      */
      GenericWebApplicationContext applicationContext = new GenericWebApplicationContext();  
       // Spring Container HelloController Bean등록 클래스의 구성정보 메타정보를 넘겨준다.  
       applicationContext.registerBean(HelloController.class);  
       // Spring Container SimpleHelloService를 Bean등록 클래스의 구성정보 메타정보를 넘겨준다.  
       applicationContext.registerBean(SimpleHelloService.class);  
       // ApplicationContext가 refresh메소드를 통해 빈 오브젝트를 생성합니다.  
       applicationContext.refresh();  
  
       /*  
          Spring Boot에서 Tomcat Sublet 컨테이너를 내장해서 프로그앰에서 코드로
          쉽게 사용할 수 있도록 제공하는 클래스 TomcatServletWebServerFactory
      */
      TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();  
  
       /*  
          웹서버 서블릿 컨테이너를 생성하는 함수 serverFactory.getWebServer()
          리턴타입이 디폴트로 설정한 Tomcat이라는 명칭은 사라지고  WebServer명칭으로 된이유는
          스프링 부트가 톰캣 외에 제티나 언더토우같은 다양한 서블릿 컨테이너를 지원할 수 있고
          지원하되 일관된 방식으로 사용할 수 있도록 동작하게할 수 있도록 추상화 해놨기 때문이다.
      */  
       WebServer webServer = serverFactory.getWebServer(new ServletContextInitializer() {  
          /*  
             serverFactory를 통해서 서블릿 컨테이너가 생성되었다면 서블릿 컨테이너에 
             서블릿을 등록한다. 서블릿을 등록하는건 webserver생성시 ServletContextInitializer을
             구현하는 객체를 매개변수로 전달하면 된다.
         */
         @Override  
          public void onStartup(ServletContext servletContext) throws ServletException {  
             //  서블릿 등록  
             servletContext.addServlet("dispatcherServlet",  
                // DispatcherServlet 등록  
                new DispatcherServlet(applicationContext)  
                ).addMapping("/*");  
          }  
       });  
  
       /*  
          Servlet 컨테이너 동작 함수
      */
      webServer.start();  
    }  
  
}

 

통합된 코드

package com.tobyspring.tobyspringboot;  
  
  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;  
import org.springframework.boot.web.server.WebServer;  
import org.springframework.boot.web.servlet.ServletContextInitializer;  
import org.springframework.web.context.support.GenericWebApplicationContext;  
import org.springframework.web.servlet.DispatcherServlet;  
  
public class TobyspringbootApplication {  
  
    public static void main(String[] args) {  
  
       /*  
          Spring Container 구현, DispatcherServlet을 사용하기 위해서는
          GenericApplicationContext이 아닌 GenericWebApplicationContext 형식으로 생성해야합니다.
      */
      GenericWebApplicationContext applicationContext = new GenericWebApplicationContext() {  
          @Override  
          protected void onRefresh() {  
             super.onRefresh();  
             /*  
          Spring Boot에서 Tomcat Sublet 컨테이너를 내장해서 프로그앰에서 코드로
          쉽게 사용할 수 있도록 제공하는 클래스 TomcatServletWebServerFactory
          */
          TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();  
  
       /*  
          웹서버 서블릿 컨테이너를 생성하는 함수 serverFactory.getWebServer()
          리턴타입이 디폴트로 설정한 Tomcat이라는 명칭은 사라지고  WebServer명칭으로 된이유는
          스프링 부트가 톰캣 외에 제티나 언더토우같은 다양한 서블릿 컨테이너를 지원할 수 있고
          지원하되 일관된 방식으로 사용할 수 있도록 동작하게할 수 있도록 추상화 해놨기 때문이다.
      */  
             WebServer webServer = serverFactory.getWebServer(servletContext -> {  
                // DispatcherServlet등록  
                servletContext.addServlet("dispatcherServlet",  
                   new DispatcherServlet(this))  
                   .addMapping("/*");  
             });  
  
       /*  
          Servlet 컨테이너 동작 함수 
      */
      webServer.start();  
  
          }  
       };  
       // Spring Container HelloController Bean등록 클래스의 구성정보 메타정보를 넘겨준다.  
       applicationContext.registerBean(HelloController.class);  
       // Spring Container SimpleHelloService를 Bean등록 클래스의 구성정보 메타정보를 넘겨준다.  
       applicationContext.registerBean(SimpleHelloService.class);  
       // ApplicationContext가 refresh메소드를 통해 빈 오브젝트를 생성합니다.  
       applicationContext.refresh();  
  
  
    }  
  
}