Cách Spring Boot Tích Hợp Máy Chủ Web Tomcat Nhúng

Việc tích hợp máy chủ web Tomcat vào ứng dụng Spring Boot không yêu cầu cấu hình thủ công hay triển khai bên ngoài — toàn bộ quá trình được điều khiển tự động thông qua cơ chế cấu hình có điều kiện, khởi tạo thành phần và quản lý vòng đời. Dưới đây là phân tích kỹ thuật về cách Spring Boot xây dựng, cấu hình và kích hoạt Tomcat nhúng trong thời gian chạy.

1. Cơ chế chọn máy chủ dựa trên phụ thuộc lớp

Khi thêm spring-boot-starter-web vào dự án Maven/Gradle, hệ thống tự động kéo theo spring-boot-starter-tomcat, bao gồm các thư viện như tomcat-embed-core, tomcat-embed-el, và tomcat-embed-websocket. Trong giai đoạn khởi động, Spring Boot đọc tệp META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (hoặc spring.factories ở phiên bản cũ hơn) để đăng ký lớp ServletWebServerFactoryAutoConfiguration. Lớp này chỉ được kích hoạt nếu:

  • Ứng dụng đang chạy trong môi trường servlet (@ConditionalOnWebApplication(type = Type.SERVLET)),
  • Và lớp ServletRequest tồn tại trong classpath (@ConditionalOnClass(ServletRequest.class)).

Ngoài ra, Spring Boot sử dụng @Import({TomcatServletWebServerFactoryConfiguration.class, ...}) để nạp các cấu hình cụ thể cho từng máy chủ. Việc lựa chọn giữa Tomcat, Jetty hay Undertow được thực hiện động: nếu Tomcat.class có mặt trong classpath thì TomcatServletWebServerFactory được ưu tiên; ngược lại, hệ thống sẽ thử nghiệm các factory tương ứng khác.

2. Khởi tạo và thiết lập đối tượng Tomcat

Quá trình bắt đầu từ ServletWebServerApplicationContext, nơi phương thức onRefresh() được ghi đè để gọi createWebServer(). Hàm này lấy một factory (thường là TomcatServletWebServerFactory) rồi gọi getWebServer(...) để tạo máy chủ:

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat server = new Tomcat();
    File tempDir = determineTempDir();
    server.setBaseDir(tempDir.getAbsolutePath());

    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    connector.setPort(getPort());
    connector.setUriEncoding(getUriEncoding());
    connector.setProperty("connectionTimeout", String.valueOf(getConnectionTimeout().toMillis()));

    server.getService().addConnector(connector);
    server.setConnector(connector);

    prepareEmbeddedContext(server.getHost(), initializers);
    
    return new TomcatWebServer(server, getAutoStart());
}

Trong đó:

  • determineTempDir() xác định thư mục tạm dùng làm baseDir cho Tomcat,
  • prepareEmbeddedContext() tạo TomcatEmbeddedContext, thiết lập contextPath, docBase, và gắn các ServletContextInitializer — đặc biệt là DispatcherServletRegistrationBean, đảm bảo Spring MVC được đăng ký đúng cách vào container.

3. Quy trình khởi động và đồng bộ hóa với ApplicationContext

Sau khi TomcatWebServer được trả về, hàm khởi tạo của nó gọi initialize() — dẫn đến lệnh server.start(). Lúc này, Tomcat tiến hành:

  • Khởi tạo các thành phần lõi: Service, Engine, Host, Context,
  • Khởi động các connector và thread pool,
  • Gọi ServletContextInitializer.onStartup() để đăng ký các filter, listener và servlet.

Đồng thời, ServletWebServerApplicationContext chờ đến khi Tomcat báo cáo trạng thái STARTED, sau đó tiếp tục hoàn tất chu kỳ refresh(): tải bean, xử lý @PostConstruct, khởi chạy CommandLineRunner, v.v.

4. Các điểm mở rộng chính trong mã nguồn

Thành phần Vị trí Mục đích
ServletWebServerFactoryAutoConfiguration spring-boot-autoconfigure Kích hoạt factory phù hợp dựa trên điều kiện classpath
TomcatServletWebServerFactory spring-boot-web Tạo và tùy chỉnh đối tượng Tomcat trước khi khởi động
TomcatWebServer spring-boot-web Bao bọc lifecycle của Tomcat, hỗ trợ dừng mềm (stop()), kiểm tra trạng thái
ServletWebServerApplicationContext spring-boot-web Thay thế AnnotationConfigServletWebServerApplicationContext để tích hợp máy chủ web

5. Tùy biến nâng cao

  • Thay đổi máy chủ mặc định: Loại bỏ spring-boot-starter-tomcat, thêm spring-boot-starter-jetty, và Spring Boot sẽ tự động sử dụng JettyServletWebServerFactory.

  • Cấu hình qua file:

server:
  port: 8082
  shutdown: graceful
server.tomcat:
  max-connections: 8192
  accept-count: 100
  background-processor-delay: 10
  • Tùy chỉnh lập trình:
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> secureTomcatCustomizer() {
    return factory -> {
        factory.addAdditionalTomcatConnectors(createHttpToHttpsRedirectConnector());
    };
}

private Connector createHttpToHttpsRedirectConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    connector.setScheme("http");
    connector.setPort(8080);
    connector.setSecure(false);
    connector.setRedirectPort(8443);
    return connector;
}

Thẻ: spring-boot Tomcat servlet-container auto-configuration web-server

Đăng vào ngày 5 tháng 6 lúc 01:08