+4

Giải ngố về @SpringBootApplication cách mà một ứng dụng Spring Boot được khởi tạo như thế nào

Làm thế nào Spring Boot khởi động ứng dụng của bạn?


Spring Boot là một trong những framework mạnh mẽ và phổ biến nhất cho việc xây dựng ứng dụng Java hiện đại. Nhưng bạn đã bao giờ tự hỏi điều gì thực sự xảy ra đằng sau dòng lệnh đơn giản SpringApplication.run(Main.class, args); chưa?

Uki chúng ta đi vào phân tích đoạn code sau để biết một ứng dụng Spring boot khởi tạo như thế nào nhe:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

Vai trò của annotation @SpringBootApplication và các thành phần của nó.

  • Cách mà SpringApplication.run() thực sự hoạt động.
  • Quá trình tự động cấu hình (Auto-Configuration) trong Spring Boot.
  • Cách ứng dụng quản lý các bean, cấu hình môi trường, và khởi chạy máy chủ web.

1. @SpringBootApplication Annotation

Thành phần của @SpringBootApplication:

@SpringBootApplication là một meta-annotation, bao gồm ba annotation chính:

  1. @SpringBootConfiguration:

    • Là một alias của @Configuration, chỉ định lớp này là lớp cấu hình (configuration class).
    • Cung cấp khả năng định nghĩa bean trong phương thức với @Bean.
  2. @EnableAutoConfiguration:

    • Kích hoạt tính năng Auto-Configuration của Spring Boot:
      • Spring Boot tự động cấu hình các bean dựa trên thư viện có trong classpath và cấu hình mặc định.
      • Ví dụ:
        • Nếu spring-boot-starter-web có trong classpath, Spring Boot tự động cấu hình Tomcat và DispatcherServlet.
        • Nếu có spring-boot-starter-data-jpa, Spring Boot cấu hình EntityManager, DataSource, và Hibernate.
  3. @ComponentScan:

    • Kích hoạt cơ chế component scanning:
      • Tìm kiếm các bean và component trong package hiện tại và các package con (sub-packages) dựa trên các annotation như @Component, @Service, @Repository, @Controller.
  • Đây là code mà Spring-boot họ định nghĩa cho interface SpringBootApplication:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

2. public static void main(String[] args)

Phương thức main là điểm bắt đầu (entry point) của ứng dụng Java, và cũng là nơi Spring Boot bắt đầu khởi chạy ứng dụng.


3. SpringApplication.run(Main.class, args)

SpringApplication.run() là nơi Spring Boot khởi chạy toàn bộ ứng dụng:

  1. Tạo đối tượng SpringApplication:

    • SpringApplication.run(Main.class, args) tạo một đối tượng SpringApplication dựa trên class được truyền vào (ở đây là Main.class).

    Chính xác là như thế này:

     @SpringBootApplication
     public class Main {
         public static void main(String[] args) {
             SpringApplication app = new SpringApplication(Main.class);
             app.run(args);
         }
     }
    
    • Xác định loại ứng dụng (WebApplication hoặc CLI Application) dựa trên classpath:
      • Mặc định trong gói spring-boot-starter-web bao gồm spring-web hoặc spring-webmvc, nó sẽ khởi tạo ứng dụng web.

      • Bạn có thể set WebApplicationType bằng cách:

        Có 3 loại WebApplicationType: NONE | SERVLET | REACTIVE

        app.setWebApplicationType(WebApplicationType.SERVLET);
        
  2. Cấu hình môi trường (Environment):

    • Spring tự động load các thông tin từ application.properties, application.yml, hoặc biến môi trường hệ thống.

    • Xác định cấu hình active profiles (nếu có).

      Active profiles là các nhóm cấu hình xác định cách ứng dụng hoạt động trong các môi trường khác nhau (ví dụ: dev, qa, prod).

      Ví dụ trong file build.gradle của bạn có dòng cấu hình sau:

      ext {
            jvmArgs = ['-Dspring.profiles.active=dev']
      }
      

      hoặc bạn có thể truyền qua args bằng: java -jar myapp.jar --spring.profiles.active=dev

      Và nếu bạn có file application-dev.properties trong Resources thì Spring sẽ load theo thứ tự application-dev.properties đến application.properties và các thuộc tính trong application.properties (có thể xem như là root config) nó sẽ ghi đè các thuộc tính ở các file config thứ cấp còn lại, trong trường hợp này là: application-dev.properties

  3. Khởi tạo ApplicationContext:

    • Dựa trên loại ứng dụng, Spring Boot tạo ApplicationContext phù hợp:
      • WebApplicationContext: Nếu là ứng dụng web.
      • AnnotationConfigApplicationContext: Nếu không phải web.
    • ApplicationContext quản lý toàn bộ lifecycle của ứng dụng Spring:
      • Tạo, quản lý và khởi tạo các bean.
      • Xử lý dependency injection.
  4. Tự động cấu hình (Auto-Configuration):

    • Dựa trên @EnableAutoConfiguration, Spring Boot tìm kiếm và kích hoạt các cấu hình mặc định trong META-INF/spring.factories.

    • Ví dụ khi trong dependencies của bạn có gói spring-boot-starter-data-jpa thì ứng dụng sẽ tự động tìm và load các config của datasource nếu bạn không có config thì ứng dụng sẽ bị lỗi và không start được.

    • Nếu bạn không muốn tự động cấu hình ví dụ như data-jpa thì bạn có thể tắt đi bằng cách sau:

    @SpringBootApplication
    @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
    public class Main {
        public static void main(String[] args) {
        ...
        }
    }
    
  5. Kích hoạt Component Scan:

    • Dựa trên @ComponentScan, tìm kiếm các lớp có annotation như @Component, @Service, @Repository, @Controller trong package hiện tại và sub-packages.
  6. Khởi chạy ApplicationContext:

    • ApplicationContext khởi tạo tất cả các bean, xử lý injection, và thực hiện các lifecycle callback như:
      • @PostConstruct trên bean.
      • Các listener đăng ký qua ApplicationListener.
  7. Kích hoạt các ApplicationRunnerCommandLineRunner:

    • Nếu có các bean implement ApplicationRunner hoặc CommandLineRunner, chúng được chạy sau khi ApplicationContext khởi tạo.
  8. Khởi chạy máy chủ Web (nếu là ứng dụng web):

    • Tự động khởi chạy một máy chủ nhúng hay còn gọi là embedded server (Tomcat, Jetty, hoặc Undertow) để xử lý HTTP request. Mặc định SpringBoot sẽ khởi tạo một Tomcat server, bạn cũng có thể custom lại embedded server này.
    • Tạo DispatcherServlet để định tuyến các request đến controller tương ứng.

Quá trình khởi động của Spring Boot

Tóm tắt các bước chính khi chạy SpringApplication.run():

  1. Khởi tạo SpringApplication:

    • Xác định loại ứng dụng (web hoặc non-web).
    • Chuẩn bị các cấu hình môi trường.
  2. Khởi tạo ApplicationContext:

    • Tạo ApplicationContext phù hợp.
    • Quét và khởi tạo các bean từ @ComponentScan.
  3. Tự động cấu hình:

    • Tải các cấu hình mặc định từ spring.factories.
  4. Khởi chạy ứng dụng:

    • Kích hoạt các bean lifecycle.
    • Bắt đầu máy chủ web (nếu cần).

Kết luận

Tadaa thì chúng ta đã đi qua cách mà một ứng dụng Spring boot được khởi tạo như thế nào, tuy nó đơn giản nhưng bên dưới, Spring Boot tự động thực hiện nhiều công việc nặng nhọc cho chúng ta như tự động cấu hình, khởi tạo bean, và quản lý lifecycle của ứng dụng.

Các bài tiếp tiếp theo chúng ta sẽ đi sâu chi tiết hơn về các thành phần trên nhé, nếu mn thấy bài viết dễ hiểu thì hãy vote giúp mình để mình có động lực viết thêm nhiều bài viết hay hơn, chất lượng hơn.

Cảm ơn mọi người và hẹn gặp lại mọi người trong các bài viết tiếp theo.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.