Giới thiệu và kiến trúc Spring Boot

Xem thử

Một nhóm bốn người dựng dịch vụ đầu tiên của họ bằng Spring Framework thuần. Sau hai tuần họ có một ứng dụng chạy được, và một thư mục config với mười bốn lớp: cấu hình Tomcat, đăng ký DispatcherServlet, bộ chuyển đổi JSON, bộ xử lý lỗi, mã hóa ký tự, và những thứ khác mà mỗi người chép từ một dự án cũ. Dịch vụ thứ hai bắt đầu bằng việc chép nguyên thư mục đó, và mọi lỗi cấu hình của dịch vụ đầu tiên đi theo. Một người mới vào nhóm hỏi "tại sao chỗ này đặt thế?" và không ai trả lời được, vì cấu hình là kết quả của nhiều lần thử và sai.

Spring Boot sinh ra để giải đúng tình huống này: phần lớn cấu hình hạ tầng web, dữ liệu, bảo mật là giống nhau giữa các dự án, nên để framework chọn mặc định hợp lý theo những gì có trên classpath, và chỉ yêu cầu bạn viết phần khác biệt. Bài này kiểm kê điều đó bằng số liệu: bạn sẽ chạy một ứng dụng Boot nhỏ, in ra những gì Boot đã tự cấu hình, và đóng gói nó thành một tệp chạy được.

Sau bài này, bạn có thể

  • Nêu Spring Boot thêm gì lên trên Spring Framework: starter, auto-configuration, server nhúng, đóng gói thực thi.
  • Đọc báo cáo điều kiện của Boot để biết auto-configuration nào đã được áp dụng và vì sao.
  • So sánh ứng dụng Boot với ứng dụng Spring MVC dựng bằng tay ở các bài trước.
  • Đóng gói ứng dụng thành executable jar và hiểu cấu trúc của nó.

Chuẩn bị môi trường và dự án

Mọi thí nghiệm của bài chạy trong một dự án Maven nhỏ. Mục này nói rõ cần cài gì, khai báo thư viện nào và cấu hình ra sao; mã của từng lớp được trình bày ngay tại chỗ nó được dùng, kèm giải thích, nên bạn không cần mở nơi nào khác. Số liệu và thông báo lỗi trong bài được đo từ chính mã đó (JDK 17, Spring Boot 4.1.1, Spring Framework 7.0.9, Tomcat 11.0.24). Bản sao lưu cả dự án nằm ở kho mã của khóa học, thư mục 08-spring-boot/01-gioi-thieu-kien-truc, chỉ để bạn clone cho nhanh nếu không muốn tự gõ lại.

Yêu cầu môi trường

Thành phầnPhiên bản đã kiểm chứngGhi chú
JDK17 trở lênjava -version
Maven3.9 trở lênmvn -v
Spring Boot4.1.1khai báo ở parent trong pom.xml; quản lý Spring Framework 7.0.9 và Tomcat 11.0.24
Dockerkhông cầnbài này không dùng cơ sở dữ liệu

Cấu trúc

text
01-gioi-thieu-kien-truc/
  pom.xml                       parent spring-boot-starter-parent 4.1.1, hai starter
  src/main/java/dev/canhdominich/boot/tour/
    Application.java            @SpringBootApplication và main
    HelloController.java        @RestController với GET /hello
    TourDemo.java               khởi động ứng dụng và in bản kiểm kê

Khai báo thư viện: pom.xml

pom.xml

Dự án Maven dùng spring-boot-starter-parent 4.1.1 làm parent: parent quản lý phiên bản của mọi thư viện (Spring Framework 7.0.9, Tomcat 11.0.24, Jackson, và nhiều thứ khác) nên các dependency không ghi số phiên bản. spring-boot-starter-webmvc kéo Spring MVC, Tomcat nhúng và Jackson; spring-boot-starter-test phục vụ kiểm thử. spring-boot-maven-plugin đóng gói ứng dụng thành executable jar và cho phép mvn spring-boot:run.

xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.1.1</version>
        <relativePath/>
    </parent>

    <groupId>dev.canhdominich.javabackend</groupId>
    <artifactId>boot-tour</artifactId>
    <version>1.0.0</version>
    <name>Spring Boot - Gioi thieu va kien truc</name>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Một ứng dụng Spring Boot nhỏ nhất

Một dự án Boot bắt đầu từ pom.xml. Điểm khác so với các lab trước: dự án khai báo spring-boot-starter-parent làm parent, và từ đó không cần ghi số phiên bản của từng thư viện; phiên bản do Boot quản lý một cách đồng bộ (Spring Framework 7.0.9, Tomcat 11.0.24 và các thư viện khác đều đi theo Boot 4.1.1). Phụ thuộc là hai starter: spring-boot-starter-webmvc (kéo Spring MVC, Tomcat nhúng, Jackson và những gì ứng dụng web cần) và spring-boot-starter-test (cho kiểm thử). Plugin spring-boot-maven-plugin đóng gói ứng dụng thành tệp chạy được ở mục sau.

Phần mã chỉ có hai lớp. Application có @SpringBootApplication và hàm main gọi SpringApplication.run:

src/main/java/dev/canhdominich/boot/tour/Application.java

java
package dev.canhdominich.boot.tour;

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

@SpringBootApplication
public class Application {

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

@SpringBootApplication là ba annotation gộp lại: @Configuration (lớp này có thể khai báo bean), @ComponentScan (quét package của lớp để tìm @Component, @RestController...) và @EnableAutoConfiguration (bật cơ chế tự cấu hình dựa trên classpath).

HelloController là một controller REST, giống những gì bạn viết ở bài MVC:

src/main/java/dev/canhdominich/boot/tour/HelloController.java

java
package dev.canhdominich.boot.tour;

import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public Map<String, String> hello() {
        return Map.of("message", "Xin chao Spring Boot");
    }
}

Trong cả hai lớp không có dòng nào tạo Tomcat, đăng ký DispatcherServlet hay chọn bộ chuyển đổi JSON. Phần dưới đo xem ai đã làm những việc đó.

Kiểm kê những gì Boot đã tự cấu hình

TourDemo khởi động chính Application với cổng ngẫu nhiên (server.port=0), rồi hỏi context: phiên bản, những bean do mã của bạn khai báo, những auto-configuration đã áp dụng hoặc không, các bean xử lý yêu cầu, rồi gửi hai yêu cầu HTTP thật. Nguồn thông tin chính là ConditionEvaluationReport, bản ghi mà Boot lưu về từng điều kiện đã đánh giá:

src/main/java/dev/canhdominich/boot/tour/TourDemo.java

java
package dev.canhdominich.boot.tour;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.web.server.context.WebServerApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.SpringVersion;
import org.springframework.web.servlet.HandlerMapping;

public class TourDemo {

    public static void main(String[] args) throws Exception {
        run().forEach(System.out::println);
    }

    public static List<String> run() throws Exception {
        List<String> lines = new ArrayList<>();
        long start = System.nanoTime();
        SpringApplication application = new SpringApplication(Application.class);
        application.setDefaultProperties(java.util.Map.of("server.port", "0", "spring.main.banner-mode", "off", "logging.level.root", "WARN"));
        try (ConfigurableApplicationContext context = application.run()) {
            long millis = (System.nanoTime() - start) / 1_000_000;
            lines.add("spring boot " + SpringBootVersion.getVersion() + ", spring framework " + SpringVersion.getVersion() + ", tomcat " + org.apache.catalina.util.ServerInfo.getServerNumber());
            String[] beanNames = context.getBeanDefinitionNames();
            Set<String> yours = Arrays.stream(beanNames)
                    .filter(name -> context.getBean(name).getClass().getName().startsWith("dev.canhdominich"))
                    .collect(Collectors.toCollection(TreeSet::new));
            lines.add("beans in the context: " + (beanNames.length > 100 ? "more than 100" : beanNames.length) + ", declared by this project's classes: " + yours);
            ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
            List<String> matched = report.getConditionAndOutcomesBySource().entrySet().stream()
                    .filter(entry -> entry.getValue().isFullMatch())
                    .map(entry -> entry.getKey())
                    .filter(name -> name.endsWith("AutoConfiguration"))
                    .map(name -> name.substring(name.lastIndexOf('.') + 1))
                    .sorted()
                    .toList();
            lines.add("auto-configurations that applied: " + matched.size() + " " + matched);
            for (String name : List.of("DispatcherServletAutoConfiguration", "TomcatServletWebServerAutoConfiguration", "WebMvcAutoConfiguration", "JacksonAutoConfiguration", "HttpMessageConvertersAutoConfiguration", "ErrorMvcAutoConfiguration")) {
                lines.add("  " + name + ": " + (matched.contains(name) ? "applied" : "not applied"));
            }
            List<String> skipped = report.getConditionAndOutcomesBySource().entrySet().stream()
                    .filter(entry -> !entry.getValue().isFullMatch())
                    .filter(entry -> entry.getKey().endsWith("AutoConfiguration"))
                    .map(entry -> entry.getKey().substring(entry.getKey().lastIndexOf('.') + 1))
                    .sorted()
                    .toList();
            lines.add("auto-configurations whose conditions did not match: " + skipped.size() + " " + skipped);
            lines.add("handler mappings: " + new TreeSet<>(context.getBeansOfType(HandlerMapping.class).keySet()));
            int port = ((WebServerApplicationContext) context).getWebServer().getPort();
            lines.add("embedded server listening on a port chosen by the OS: " + (port > 0));
            lines.add(call(port, "/hello"));
            lines.add(call(port, "/nope"));
            lines.add("startup under 20 seconds: " + (millis < 20_000));
        }
        return lines;
    }

    private static String call(int port, String path) throws Exception {
        HttpResponse<String> response = HttpClient.newHttpClient().send(
                HttpRequest.newBuilder(URI.create("http://127.0.0.1:" + port + path)).GET().build(), HttpResponse.BodyHandlers.ofString());
        String body = response.body().replaceAll("\"timestamp\":\"[^\"]*\",?", "");
        return "GET " + path + " -> " + response.statusCode() + " " + response.headers().firstValue("Content-Type").orElse("") + " " + body;
    }
}

Chạy bằng mvn -q compile exec:java -Dexec.mainClass=dev.canhdominich.boot.tour.TourDemo. Kết quả (đã bỏ log ở stderr):

text
spring boot 4.1.1, spring framework 7.0.9, tomcat 11.0.24.0
beans in the context: more than 100, declared by this project's classes: [application, helloController]
auto-configurations that applied: 11 [AopAutoConfiguration, DispatcherServletAutoConfiguration, ErrorMvcAutoConfiguration, HttpEncodingAutoConfiguration, HttpMessageConvertersAutoConfiguration, JacksonAutoConfiguration, MultipartAutoConfiguration, TaskExecutionAutoConfiguration, TaskSchedulingAutoConfiguration, TomcatServletWebServerAutoConfiguration, WebMvcAutoConfiguration]
  DispatcherServletAutoConfiguration: applied
  TomcatServletWebServerAutoConfiguration: applied
  WebMvcAutoConfiguration: applied
  JacksonAutoConfiguration: applied
  HttpMessageConvertersAutoConfiguration: applied
  ErrorMvcAutoConfiguration: applied
auto-configurations whose conditions did not match: 13 [JmxAutoConfiguration, MessageSourceAutoConfiguration, ServletHttpExchangesAutoConfiguration, ServletManagementContextAutoConfiguration, ServletMappingsAutoConfiguration, SpringApplicationAdminJmxAutoConfiguration, TomcatMetricsAutoConfiguration, TomcatReactiveManagementContextAutoConfiguration, TomcatReactiveWebServerAutoConfiguration, TomcatServletManagementContextAutoConfiguration, WebMvcHealthEndpointExtensionAutoConfiguration, WebMvcMappingsAutoConfiguration, WebMvcObservationAutoConfiguration]
handler mappings: [beanNameHandlerMapping, requestMappingHandlerMapping, resourceHandlerMapping, routerFunctionMapping, welcomePageHandlerMapping, welcomePageNotAcceptableHandlerMapping]
embedded server listening on a port chosen by the OS: true
GET /hello -> 200 application/json {"message":"Xin chao Spring Boot"}
GET /nope -> 404 application/json {"status":404,"error":"Not Found","path":"/nope"}
startup under 20 seconds: true

Đọc kết quả:

  • Phiên bản đi cùng nhau. Boot 4.1.1 mang theo Spring Framework 7.0.9 và Tomcat 11.0.24: một bộ phiên bản đã được thử cùng nhau, không phải bạn tự ghép.
  • Chỉ hai bean là của bạn. Trong cả context hơn một trăm bean, hai bean do lớp của dự án khai báo là application (lớp @SpringBootApplication cũng là một bean) và helloController. Phần còn lại đến từ Spring và từ auto-configuration.
  • Mười một auto-configuration đã áp dụng. Những cái đáng chú ý nhất chính là các thứ bạn tự viết ở bài MVC: TomcatServletWebServerAutoConfiguration dựng và khởi động Tomcat, DispatcherServletAutoConfiguration đăng ký DispatcherServlet, WebMvcAutoConfiguration đăng ký các bean chiến lược của MVC, JacksonAutoConfiguration và HttpMessageConvertersAutoConfiguration lo việc chuyển đối tượng thành JSON, ErrorMvcAutoConfiguration lo trang lỗi.
  • Mười ba cái không khớp điều kiện. Ví dụ TomcatReactiveWebServerAutoConfiguration không áp dụng vì ứng dụng không dùng web reactive. Báo cáo điều kiện cho bạn biết lý do từng cái bị bỏ qua, đó là công cụ để trả lời "vì sao bean này có (hoặc không có)".
  • Các bean xử lý yêu cầu. Danh sách handler mappings gồm đủ những bean bạn thấy ở bài MVC (requestMappingHandlerMapping, beanNameHandlerMapping, routerFunctionMapping) cộng thêm vài cái của Boot (resourceHandlerMapping cho tệp tĩnh, welcomePageHandlerMapping).
  • Server nhúng đã chạy. Cổng do hệ điều hành chọn, và GET /hello trả JSON. Controller trả một Map, Jackson do Boot cấu hình chuyển nó thành JSON.
  • Lỗi có thân JSON. GET /nope trả 404 với thân {"status":404,"error":"Not Found","path":"/nope"}, nhờ ErrorMvcAutoConfiguration. Ở lab Spring MVC thuần, cùng loại yêu cầu chỉ có mã 404 và trang lỗi mặc định của Tomcat.

Cách đọc hình: cột trái là ba việc bạn tự làm ở lab Spring MVC (tạo Tomcat, đăng ký DispatcherServlet, bật cấu hình MVC); cột phải là auto-configuration của Boot làm cùng việc và đã được ghi nhận là "applied" trong báo cáo. Nét đứt nghĩa là cùng kết quả, khác người viết.

Cách đọc hình: hàng trên: 11 auto-configuration đã áp dụng vì các lớp của chúng có trên classpath. Hàng dưới: 13 cái bị bỏ qua vì một điều kiện không thỏa. Ô bên phải nhắc rằng chỉ hai bean trong hơn một trăm bean là do mã của bạn khai báo.

Đang tải Animation…

Cách Boot quyết định: starter, classpath và điều kiện

Chuỗi quyết định có ba mắt xích, và hiểu chúng giúp bạn làm chủ Boot thay vì xem nó là phép màu:

  1. Starter chọn thư viện. Một starter là một dependency "gói": spring-boot-starter-webmvc kéo spring-webmvc, Tomcat nhúng và Jackson về classpath với phiên bản đã được Boot thử cùng nhau. Bạn không phải liệt kê từng thư viện.
  2. Classpath là dữ liệu đầu vào. Mỗi auto-configuration có điều kiện: ví dụ chỉ áp dụng khi lớp DispatcherServlet có trên classpath và ứng dụng là ứng dụng web servlet. Thay đổi dependency là thay đổi những auto-configuration được áp dụng.
  3. Bạn luôn thắng. Hầu hết auto-configuration chỉ tạo bean khi bạn chưa tự khai báo một bean cùng loại (bài kế tiếp đo điều này), nên việc ghi đè là khai báo bean của riêng bạn, không phải sửa Boot.

Công cụ để kiểm tra là chính ConditionEvaluationReport (bạn vừa dùng) hoặc chạy ứng dụng với --debug để Boot in báo cáo điều kiện ra log.

Đóng gói thành executable jar

Bước cuối là phát hành. Với spring-boot-maven-plugin, mvn package tạo ra một tệp jar chạy được bằng java -jar mà không cần cài Tomcat. Kết quả đo trên máy tác giả:

text
target/boot-tour-1.0.0.jar: 19,9 MB
Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: dev.canhdominich.boot.tour.Application
Spring-Boot-Version: 4.1.1
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
số jar trong BOOT-INF/lib: 34
BOOT-INF/lib/spring-boot-4.1.1.jar, spring-boot-autoconfigure-4.1.1.jar, spring-boot-tomcat-4.1.1.jar, tomcat-embed-core-11.0.24.jar, jackson-databind-3.1.5.jar, ...
Started Application in 3.165 seconds

Cấu trúc này đáng hiểu: manifest của jar trỏ Main-Class tới JarLauncher (một lớp của Boot), không phải tới Application. JarLauncher biết cách đọc các jar lồng bên trong BOOT-INF/lib và dựng classpath từ đó, rồi gọi Start-Class là hàm main của bạn. Mã đã biên dịch của bạn nằm ở BOOT-INF/classes. Một tệp duy nhất mang theo toàn bộ thư viện: đó là lý do chạy curl http://localhost:18091/hello (cổng đặt bằng --server.port=18091) trả {"message":"Xin chao Spring Boot"} ngay sau java -jar.

Cách đọc hình: java -jar chạy JarLauncher (Main-Class), nó dựng classpath từ BOOT-INF/classes và 34 jar trong BOOT-INF/lib, rồi gọi Application.main (Start-Class).

Bài tiếp theo (đọc cấu hình) vẫn dùng đúng cơ chế này để cho bạn đổi hành vi của ứng dụng mà không đổi mã.

Cách đọc hình: hàng trên: ở lab Spring MVC thuần, 404 do Tomcat trả lời theo kiểu của nó. Hàng dưới: Boot có ErrorMvcAutoConfiguration, trả 404 kèm thân JSON mà ứng dụng client đọc được.

Quyết định thiết kế và đánh đổi

Quyết địnhLựa chọnĐánh đổi
Quản lý phiên bảnspring-boot-starter-parentNâng Boot kéo theo nhiều thư viện nâng cùng lúc
Cấu hình webAuto-configurationPhải biết cách đọc báo cáo điều kiện khi hành vi khó hiểu
Chạy ứng dụngServer nhúng, một tệp jarMỗi ứng dụng mang theo server của nó
Ghi đè mặc địnhTự khai báo bean cùng loạiCần biết bean nào do auto-configuration tạo
Phụ thuộcStarter thay vì từng thư việnKéo nhiều jar hơn mức bạn dùng thật (34 jar ở bài này)

Playbook: gặp sự cố khởi động Boot thì làm gì

Triệu chứngKiểm tra đầu tiênNguyên nhân hay gặpCách xử lý
Không biết bean này do đâuBáo cáo điều kiện (--debug)Auto-configuration tạo beanĐọc báo cáo, ghi đè bằng bean của bạn
Thiếu một tính năng mong đợiStarter có trong pom.xml khôngThiếu dependency nên điều kiện không khớpThêm starter tương ứng
Port 8080 was already in useTiến trình đang dùng cổngỨng dụng khác chiếm cổng--server.port=...
Jar chạy báo thiếu lớpPlugin đóng góiDùng mvn package mà không có spring-boot-maven-pluginGiữ plugin trong pom.xml
Controller không được quétPackage của controllerNằm ngoài package của @SpringBootApplicationĐưa controller vào cùng package hoặc package con

Lỗi thường gặp khi chạy lab

Triệu chứngNguyên nhânCách xử lý
mvn không tải được thư viện Spring BootChưa có mạngChạy lại với mạng
Port 8080 was already in useCổng 8080 đang bị chiếmjava -jar target/boot-tour-1.0.0.jar --server.port=8081

Bài thực hành

Làm trong dự án của bài (thư mục 08-spring-boot/01-gioi-thieu-kien-truc). Sau mỗi bước, so kết quả với tiêu chí nghiệm thu.

  1. Chạy bản kiểm kê. mvn -q compile exec:java -Dexec.mainClass=dev.canhdominich.boot.tour.TourDemo. Tiêu chí: số auto-configuration áp dụng là 11 và không khớp là 13, và hai yêu cầu HTTP khớp bài.
  2. Chạy như server thật. mvn -q spring-boot:run rồi curl http://localhost:8080/hello. Tiêu chí: {"message":"Xin chao Spring Boot"}; thử curl -i http://localhost:8080/nope và ghi lại mã cùng thân.
  3. Đóng gói. mvn -q package -DskipTests, rồi unzip -p target/boot-tour-1.0.0.jar META-INF/MANIFEST.MF. Tiêu chí: Main-Class là org.springframework.boot.loader.launch.JarLauncher và Start-Class là dev.canhdominich.boot.tour.Application.
  4. Xem báo cáo điều kiện của Boot. Chạy java -jar target/boot-tour-1.0.0.jar --debug và tìm phần CONDITIONS EVALUATION REPORT. Tiêu chí: tìm thấy DispatcherServletAutoConfiguration trong nhóm Positive matches và ghi lại một cái trong nhóm Negative matches cùng lý do.
  5. Đặt cổng. Chạy java -jar target/boot-tour-1.0.0.jar --server.port=8088 và gọi /hello ở cổng đó. Tiêu chí: trả 200.
  6. Chạy bộ test (tùy chọn). Bộ test nằm trong bản sao lưu ở kho mã của khóa học; sau khi clone, mvn test cho Tests run: 4, Failures: 0, Errors: 0. Dọn dẹp bằng mvn clean.

Hiểu lầm và giới hạn

  • "Spring Boot là một framework khác." Nó dùng chính Spring Framework; nó thêm cấu hình tự động, starter và đóng gói.
  • "Auto-configuration là phép màu." Mỗi cái là một lớp cấu hình có điều kiện, kiểm tra được bằng báo cáo.
  • "Boot chạy trong Tomcat bạn cài." Server nằm trong jar của ứng dụng.
  • Số đo thuộc về Spring Boot 4.1.1 (Spring Framework 7.0.9, Tomcat 11.0.24, JDK 17); số auto-configuration thay đổi theo dependency và phiên bản. Bài chưa nói về application.properties (bài sau), profile, actuator, và cách đóng gói thành Docker image.

Tự kiểm tra

  1. Nêu ba điều Spring Boot thêm lên trên Spring Framework.
  2. @SpringBootApplication gồm những annotation nào?
  3. Làm sao biết một bean do auto-configuration nào tạo ra, và vì sao một auto-configuration không được áp dụng?
  4. Main-Class trong manifest của executable jar là lớp nào, và vì sao không phải Application?
  5. Starter khác một dependency thường ở điểm nào?

Gợi ý đáp án. (1) Starter, auto-configuration theo classpath, server nhúng cùng đóng gói thực thi (và quản lý phiên bản). (2) @Configuration, @ComponentScan, @EnableAutoConfiguration. (3) Dùng báo cáo điều kiện (ConditionEvaluationReport hoặc --debug): nó ghi cả điều kiện khớp và không khớp. (4) JarLauncher, vì nó dựng classpath từ các jar lồng trong BOOT-INF/lib trước rồi mới gọi Start-Class. (5) Nó là gói nhiều dependency cùng phiên bản tương thích cho một mục đích.

Điều cần nhớ

  • Boot = Spring Framework + starter + auto-configuration + server nhúng + đóng gói thực thi.
  • Classpath là đầu vào của auto-configuration; thay dependency là thay hành vi.
  • Bạn ghi đè mặc định bằng cách tự khai báo bean; báo cáo điều kiện cho biết ai tạo gì.
  • java -jar chạy JarLauncher, nó nạp jar lồng nhau rồi gọi main của bạn.

Đọc thêm

Bài tiếp theo xem application.properties, profile và @ConfigurationProperties: các nút bạn vặn để đổi hành vi mà không sửa mã.