부트 개념과 활용-04.내장 웹 서버 이해


내장 웹 서버 이해

스프링 부트는 서버가 아닙니다. 하지만..

  • 톰캣 객체 생성
  • 포트 설정
  • 톰캣에 컨텍스트 추가
  • 서블릿 만들기
  • 톰캣에 서블릿 추가
  • 컨텍스트에 서블릿 맵핑
  • 톰캣 실행 및 대기
  • ServletWebServerFactoryAutoConfiguration (서블릿 웹 서버 생성)

이 모든 과정을 보다 상세히 또 유연하고 설정하고 실행해주는게 바로 스프링 부트의 자동 설정입니다.

또한 자동 설정되어있는 되는 서버를 커스터마이징 할 수도 있습니다.

  • TomcatServletWebServerFactoryCustomizer (서버 커스터마이징)
    • DispatcherServletAutoConfiguration
  • 서블릿 만들고 등록 등..

자동 설정이 되어있지 않다면?…

 public class Application {

    public static void main(String[] args) throws LifecycleException {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context context = tomcat.addContext("/", "/");

        HttpServlet servlet = new HttpServlet() {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                PrintWriter writer = resp.getWriter();
                writer.println("<html><head><title>");
                writer.println("hey, jaeuk tomcat");
                writer.println("</title></head>");
                writer.println("<body><h1>hi jaeuk!</h1></body");
                writer.println("</html>");
                writer.println("<html><head><title>");
            }
        };

        String serveletName = "helloServelet";
        tomcat.addServlet("/", serveletName, servlet);
        context.addServletMapping("/hello", serveletName);
        // /hello 요청이 오면 serveletName(helloServelet) 위에서 생성한 서블릿을 보여주는 것

        tomcat.start();
        tomcat.getServer().await();
    }
}

실행 결과 실행 결과

사실 이런 것들이 다 자동 설정이 되는 것입니다..

이전 포스트인 자동설정과 관련이 있는데,

스프링 부트 어플리케이션을 실행하면 톰캣이 만들어지고 서블릿이 추가되고 웹MVC 설정이 되고 띄워지는 것.

이전 포스트에서 살펴본

spring-boot-autoconfigration - META-INF - spring.factories 안을 보면

...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

...
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
...

이 존재합니다. (자동 설정)

@EnableAutoConfiguration가 자동 설정하는 리스트들 중에 ServletWebServerFactoryAutoConfiguration 이 안을 보면

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

	@Bean
	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
		return new ServletWebServerFactoryCustomizer(serverProperties);
	}

	@Bean
	@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
		return new TomcatServletWebServerFactoryCustomizer(serverProperties);
	}
...

Import 중에 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class 가 있습니다.

(EmbeddedTomcat)그 안을 한번 보면..

	@Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
			return new TomcatServletWebServerFactory();
		}
	}

이 안의 TomcatServletWebServerFactory를 보면

커넥터, 컨텍스트 설정, 클래스 로더 등등.. 설정들을 다 해주고 있습니다.

마찬가지로 서블릿도 DispatcherServletAutoConfiguration 에서 자동 설정을 해 주고 있습니다.

spring-boot-autoconfigration - META-INF - spring.factories 안을 보면

...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

...
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
...
public class DispatcherServletAutoConfiguration {
...
protected static class DispatcherServletConfiguration {
	@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
	public DispatcherServlet dispatcherServlet() {
		DispatcherServlet dispatcherServlet = new DispatcherServlet();
		dispatcherServlet.setDispatchOptionsRequest(
		this.webMvcProperties.isDispatchOptionsRequest());
		dispatcherServlet.setDispatchTraceRequest(
		this.webMvcProperties.isDispatchTraceRequest());
		dispatcherServlet.setThrowExceptionIfNoHandlerFound(
		this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
		return dispatcherServlet;
	}
...

DispatcherServletAutoConfiguration 클래스 안에서 디스패처 서블릿(DispatcherServlet)이 만들어 집니다.
(httpServlet을 상속하여 만든 스프링의 MVC의 핵심 클래스)

이 클래스 안에서 디스패서 서블릿을 만들고 (DispatcherServletConfiguration class)

서블릿 컨테이너에 등록하는 일들이 이루어집니다.(DispatcherServletRegistrationConfiguration class)

디스패처 서블릿을 만드는 것과 서블릿 컨테이너에 등록하는 두 부분이 왜 나누어져 있냐면(이 둘이 왜 떨여져 있냐?)

서블릿 컨테이너는 다 달라질수 있는데, (톰캣 등…) pom.xml 설정에 따라 다 달라지는데

서블릿은 변하지 않습니다.

그래서 둘이 분리가 되었습니다.

디스패서 서블릿은 내가 어떠한 서블릿 컨테이너를 쓰든 상관없이 얘를 만든 다음에
지금 있는 서블릿 컨테이너에 등록하는 과정이 이 안에서 벌어집니다.

예시 코드 및 더 자세한 설명은 다음 포스트에서 이어서 정리하겠습니다.




© 2019. by jaeuk