---
# System prepended metadata

title: Springboot基礎

---

# Springboot基礎
概述:Spring Boot是Spring提供的一個子專案,用於快速建立Spring應用程式

核心功能:
Spring Framework:為依賴注入、事務管理、Web 應用程式、資料存取、訊息傳遞等提供核心支援。

數據獲取:
Spring Data:提供一致的資料存取方法 - 關係、非關係、map-reduce 等。

訊息傳遞:
Spring AMQP:將核心 Spring 概念應用於基於 AMQP 的訊息傳遞解決方案的開發。

認證授權:
Spring Security:透過全面且可擴展的身份驗證和授權支援保護您的應用程式。

服務治理:
Spring Cloud:為分散式系統中的常見模式提供一組工具。對於建置和部署微服務很有用。

項目構造:
SpringBoot:對建立 Spring 應用程式採取固執己見的觀點，並讓您盡快啟動並運行。

## SpringBoot特性
## 1.起步依賴:
就是一個Maven座標，整合了完成一個功能需要德所有坐標!
![螢幕擷取畫面 2024-11-09 234538](https://hackmd.io/_uploads/SkaRX-6Wye.png)

## 2.傳遞依賴:
解決配置繁瑣的問題
![螢幕擷取畫面 2024-11-11 113658](https://hackmd.io/_uploads/B1tzhx1zkx.png)

## 3.自動配置:
遵循約定大約配置的原則，在boot程序啟動後一些bean對象會自動注入到ioc容器，不需要手動聲明，簡化開發
![螢幕擷取畫面 2024-11-09 234742](https://hackmd.io/_uploads/ryXH4WTbJe.png)
## 4.其他特性
1.內嵌的Tomcat、Jetty(無需部署WAR檔)
2.外部化配置
3.不需要XML配置(properties/yml)

## SpringBoot入門
1.創建Maven工程
2.導入spring-boot-starter-web起步依賴
```
<dependency>
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
```
3.編寫controller
```
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        System.out.println("Hello World ~");
        return "Hello World ~";
    }
}
```
4.提供啟動類
```
@SpringBootApplication
public class SpringbootStartApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootStartApplication.class, args);
    }
}
```
## 手動創建SpringBoot
## idea創建
![螢幕擷取畫面 2024-11-10 113817](https://hackmd.io/_uploads/r1177XAbyl.png)
![螢幕擷取畫面 2024-11-11 081243](https://hackmd.io/_uploads/S1gmn6Ab1l.png) 
springboot版本看最新的，像我是用3.3.5
## pom.xml
```java=  `
<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 http://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>3.3.5</version>

  </parent>

  <groupId>com.itheima</groupId>
  <artifactId>springboot-create-manual</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>springboot-create-manual</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
```
## 用到檔案
1. springboot-quickstart
2. springboot-create-manual
# 用到註解
@SpringBootApplication用來標記主應用類別的註解，它是一個組合註解，主要包含以下三個註解：
1. @Configuration - 表示該類別可以作為 Spring 的配置類。
2. @EnableAutoConfiguration - 啟用 Spring Boot 的自動配置機制，根據加入的依賴自動配置Bean。
3. @ComponentScan - 自動掃描當前包及其子包中標記了 @Component、@Service、@Repository 等註解的類，並註冊為 Spring Bean。
範例:
```
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
```
# springboot配置文件_基本使用
## application.properties:
https://docs.spring.io/spring-boot/appendix/application-properties/index.html#appendix.application-properties
![圖片1](https://hackmd.io/_uploads/S1a1wVRbJg.png)
## 修改端口號
在resources裡面的application.properties檔案裡面加入
```
server.port=9090 
server.servlet.context-path=/start
```
google就會顯示
![螢幕擷取畫面 2024-11-10 215829](https://hackmd.io/_uploads/Sy0I3N0Zyx.png)
## 配置文件格式
1. SpringBoot提供了多種屬性配置方式
    application.properties
```
server.port=9090
server.servlet.context-path=/start
```
![螢幕擷取畫面 2024-11-10 220849](https://hackmd.io/_uploads/rkUjAEC-Je.png)

2. application.yml / application.yaml :層次清晰、關注數據
```
server:
port: 9191
servlet:
context-path: /start2
```
![螢幕擷取畫面 2024-11-10 220941](https://hackmd.io/_uploads/H1xCRVR-Jx.png)
# yml配置信息書寫與獲取
1. 三方技術配置信息:假設我們需要配置一個資料庫和Redis這樣的三方技術，可以在application.yml中撰寫配置資訊：
```
//檔案yaml
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: pass
    driver-class-name: com.mysql.cj.jdbc.Driver

  redis:
    host: localhost
    port: 6379
    password: redispass

```
2. 自定義配置信息:假設我們要配置一些自定義的設定，例如API金鑰和應用模式等，可以直接在application.yml裡添加自定義配置項：
```
//檔案yaml
# application.yml
custom:
  api:
    key: YOUR_API_KEY
    secret: YOUR_API_SECRET
  app:
    mode: development
    max-retries: 3


用到檔案
1.springboot-config-file (重點:jakarta.mail)
```
//pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.itheima</groupId>
  <artifactId>springboot-config-file</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>springboot-config-file</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <!-- Web development dependencies -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>2.7.15</version> <!-- Add the required Spring Boot version here -->
    </dependency>
    <!-- Java mail dependency -->
    <dependency>
      <groupId>org.eclipse.angus</groupId>
      <artifactId>jakarta.mail</artifactId>
      <version>2.0.1</version>
    </dependency>

<!-- JUnit dependency for testing -->

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
```
## yml配置資訊書寫與獲取
用到文件:springboot-config-file
### 1.
![螢幕擷取畫面 2024-11-11 082131](https://hackmd.io/_uploads/r16SCpRWJg.png)!
轉到
```
//application
email.user=593140521@qq.com
email.code=jfejwezhcrzcbbbb
email.host=smtp.qq.com
email.auth=true
```
```
//application.yml  書寫
    email:
    user:593140521@qq.com
    code:jfejwezhcrzcbbbb
    host:smtp.qq.com
    auth:true
```
解釋:
1. 值前邊必須有空格，作為分隔符
2. 使用空格作為縮排表示層級關係，相同的層級左側對齊
### 2.

```
//application.yml  獲取
    email:
    user:593140521@qq.com
    code:jfejwezhcrzcbbbb
    host:smtp.qq.com
    auth:true
```
![螢幕擷取畫面 2024-11-11 085515](https://hackmd.io/_uploads/r1-vICAZ1e.png)

解釋:
@value("${鍵名}")
### 3.

```
//application.yml  獲取
    email:
    user:593140521@qq.com
    code:jfejwezhcrzcbbbb
    host:smtp.qq.com
    auth:true
```
![螢幕擷取畫面 2024-11-11 083922](https://hackmd.io/_uploads/SJFPMR0-kg.png)
解釋:
@ConfigurationProperties(prefix="前綴")實體類別的成員變數名稱與設定檔中的鍵名保持一致，像是上面的email
# 整合mybatis
用到檔案:springboot- mybatis

![螢幕擷取畫面 2024-11-11 092243](https://hackmd.io/_uploads/Byij2CAW1x.png)
```
<?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>3.3.5</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.itheima</groupId>
  <artifactId>springboot-mybatis</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>springboot-mybatis</name>
  <description>springboot-mybatis</description>
  <properties>
    <java.version>17</java.version>
  </properties>
  <dependencies>

    <!--mysql驱动依赖-->
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
    </dependency>

    <!--mybatis的起步依赖-->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>3.0.0</version>
    </dependency>

    <!--自定义的mybatis起步依赖-->
    <!-- <dependency>
         <groupId>com.itheima</groupId>
         <artifactId>dmybatis-spring-boot-starter</artifactId>
         <version>1.0-SNAPSHOT</version>
     </dependency>-->


    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</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>
```
## 查詢User表指定id的數據，回應給瀏覽器
![image](https://hackmd.io/_uploads/BkvZpCCbJe.png)
開啟順序為:瀏覽器 > usercontroller > userservice > usermapper > 數據庫
# Bean管理
## Bean掃描
1. 標籤：<context:component-scan base-package="com.itheima"/>
2. 註解：@ComponentScan(basePackages = "com.itheima")
![螢幕擷取畫面 2024-11-11 093652](https://hackmd.io/_uploads/SJtJe1Jf1x.png)
![image](https://hackmd.io/_uploads/B1FvekJfJg.png)
解釋:
1. @SpringBootApplication: 是一個複合註解，包含了三個主要註解：
@Configuration：表明這個類是一個Spring的配置類。
@EnableAutoConfiguration：啟用Spring Boot的自動配置機制。
@ComponentScan：讓Spring掃描指定包路徑下的元件（如@Component、@Service、@Repository等）並註冊到Spring上下文中。
2. @ComponentScan: 單獨使用時，讓Spring自動掃描指定包路徑下的Bean。@ComponentScan可以指定要掃描的包範圍，這樣可以控制Spring只掃描特定的包，提高啟動速度或避免不必要的Bean註冊。

總結： @SpringBootApplication已經包含了@ComponentScan的功能，通常會自動掃描與主啟動類（標註@SpringBootApplication的類）相同包或其子包下的元件，所以SpringBoot預設掃描啟動類別所在的套件及其子包

##  Bean註冊 
![螢幕擷取畫面 2024-11-11 100147](https://hackmd.io/_uploads/HytzUJ1zJe.png)
如果要註冊的bean物件來自於第三方（不是自訂的），是無法用 @Component 及衍生註解聲明bean的
![image](https://hackmd.io/_uploads/HkrJ_BgGyx.png)
用到檔案:
1.springboot-register
2.資料裡面的bean註冊資料

資料載入到本機方式:
1.打開bean註冊腳本，複製裡面內容到終端機上，但每一個人的本機位置不一樣，所以腳本裡面下面這一段要修改
![螢幕擷取畫面 2024-11-12 112039](https://hackmd.io/_uploads/ryhe5rezJe.png)

## 註解:
1. @Bean:註解用於標記一個方法，該方法實例化、配置並初始化一個新的 Bean，並將其註冊到 Spring 容器中進行管理。
通常，使用 @Bean 註解的方法定義在 @Configuration 註解的類別中。
```java=
@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
```
解釋:myService 被注册为一个 Spring Bean，可以通过 @Autowired 注解在应用的其他地方进行注入。

2. @Import:
一. 導入配置類  
二.導入 ImportSelector 介面實作類別
```
@Configuration
@Import({AppConfig.class, DataSourceConfig.class})
public class MainConfig {
    // 其他的 Bean 定義可以放在这里
}
```

三. 解用於將額外的配置類別匯入到 Spring 用上下文中。這對於模組化配置或導入外部庫的配置非常有用。
![螢幕擷取畫面 2024-11-11 215656](https://hackmd.io/_uploads/S12fAKyMke.png)
[![螢幕擷取畫面 2024-11-11 215711](https://hackmd.io/_uploads/rkimAKyzJx.png)
](https://)



## 註冊條件
SpringBoot提供了設定註冊生效條件的註解 @Conditional
![螢幕擷取畫面 2024-11-13 102505](https://hackmd.io/_uploads/B17NCYZzyl.png)

@ConditionalOnProperty:
1. 根據設定檔中的屬性值決定是否要載入某個 Bean。
2. 通常用於根據已套用的設定檔（如 application.properties 或 application.yml）中的某個屬性來控制 Bean 的建立。
```
@Configuration
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class FeatureConfig {
     // 如果 feature.enabled=true，則載入該組態類
}
```
@ConditionalOnMissingBean 和 @ConditionalOnBean:
1. @ConditionalOnMissingBean：當容器中不存在某個特定類型的 Bean 時，才會載入該 Bean。
2. @ConditionalOnBean：當容器中存在某個特定類型的 Bean 時，才會載入該 Bean。
```
@Configuration
public class MyConfig {
    @Bean
    @ConditionalOnMissingBean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
//在上面的範例中，MyService Bean 只有在容器中不存在相同類型的 Bean 時才會被建立。
```
@ConditionalOnClass 和 @ConditionalOnMissingClass:
1. @ConditionalOnClass：當類別路徑中存在指定的類別時，才會載入該 Bean。
2. @ConditionalOnMissingClass：當類別路徑中不存在指定的類別時，才會載入該 Bean。
3. 通常用於根據特定的類別或依賴是否存在來載入配置或元件。
```
@Configuration
@ConditionalOnClass(name = "com.example.SomeLibrary")
public class SomeLibraryConfig {
     // 只有當 SomeLibrary 類別在類別路徑中時，才會載入該設定類
}
```
@ConditionalOnExpression:
使用 SpEL 表達式來決定是否載入 Bean。適合複雜的條件判斷。
```
@Configuration
@ConditionalOnExpression("${feature.enabled:true} and ${feature.level} > 5")
public class AdvancedFeatureConfig {
    // 只有當 feature.enabled 為 true 且 feature.level 大於 5 時，才會載入該配置
```
@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication:
1. @ConditionalOnWebApplication：當應用程式是 Web 應用程式時，才會載入該 Bean。
2. @ConditionalOnNotWebApplication：當應用程式不是一個 Web 應用程式時，才會載入該 Bean。
```
@Configuration
@ConditionalOnWebApplication
public class WebConfig {
    // 只有在 Web 應用程式環境中，才會載入該配置
}
```
@ConditionalOnJava:
根據運行時的 Java 版本來決定是否載入 Bean。
```
@Configuration
@ConditionalOnJava(range = ConditionalOnJava.Range.EQUAL_OR_NEWER, value = JavaVersion.ELEVEN)
public class Java11Config {
     // 只有在 Java 11 或更新的版本上才會載入該配置
}
```
@ConditionalOnResource:
當類別路徑下存在指定的資源檔案時，才載入 Bean。
```
@Configuration
@ConditionalOnResource(resources = "classpath:special-config.xml")
public class ResourceConfig {
     // 如果類別路徑中存在 special-config.xml 文件，則載入該配置
}
```

總結:
@ConditionalOnProperty：基於設定檔屬性。
@ConditionalOnMissingBean 和 @ConditionalOnBean：基於容器中是否有特定 Bean。
@ConditionalOnClass 和 @ConditionalOnMissingClass：基於類別路徑中是否存在特定類別。
@ConditionalOnExpression：基於 SpEL 表達式。
@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication：基於應用程式是否為 Web 應用。
@ConditionalOnJava：基於 Java 版本。
@ConditionalOnResource：基於類別路徑下是否存在指定資源。

# Spring Boot IoC（Inversion of Control）
IoC 的主要目的是反轉控制權，換句話說，Spring 容器負責對象的創建和管理，而開發者不需要手動創建和管理對象。開發者只需定義需要哪些對象（依賴），Spring 容器會在運行時將這些依賴注入到相應的對象中。

## IoC 主要有三種形式：
1. Constructor Injection（建構子注入）
2. Setter Injection（Setter方法注入）
3. Field Injection（字段注入）

## Spring Boot IoC 實現原理
Spring Boot 使用 Spring IoC 容器，這是 Spring 框架的核心部分。容器使用 Java 配置或 XML 配置來描述 Bean（對象）的定義和它們之間的依賴關係。Spring Boot 進一步簡化了這個過程，使得開發者不需要額外的配置，只需要使用註解來自動注入對象。

## 範例
1. 創建 Printer 介面和實現類:首先定義 Printer 介面，然後創建它的實現類，例如 HpPrinter 和 CanonPrinter
```
public interface Printer {
    void print(String content);
}

@Component
public class HpPrinter implements Printer {
    @Override
    public void print(String content) {
        System.out.println("Printing with HP Printer: " + content);
    }
}

@Component
public class CanonPrinter implements Printer {
    @Override
    public void print(String content) {
        System.out.println("Printing with Canon Printer: " + content);
    }
}

```
解釋:@Component 註解告訴 Spring 容器，這些類應該被管理為 Spring 的 Bean。

2.  創建 Teacher 類，並注入 Printer:Teacher 類依賴 Printer，我們可以使用建構子注入的方式來實現這種依賴。
```
@Component
public class Teacher {
    private Printer printer;

    // 使用建構子注入
    @Autowired
    public Teacher(Printer printer) {
        this.printer = printer;
    }

    public void teach(String lessonContent) {
        System.out.println("Teaching: " + lessonContent);
        printer.print(lessonContent);  // 使用注入的 Printer
    }
}

```
解釋: @Autowired 註解來告訴 Spring，這個建構子需要注入一個 Printer 依賴。Spring 會自動選擇一個 Printer 實現類來注入進來。

3. 創建 Spring Boot 應用主類:創建一個 Spring Boot 應用的主類來啟動容器並調用 Teacher 類的方法。
```
@SpringBootApplication
public class SpringBootIoCDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootIoCDemoApplication.class, args);
    }
}
```
解釋:@SpringBootApplication 是一個組合註解，包含了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 等註解，這樣 Spring Boot 就會自動掃描並加載位於當前包及其子包中的所有組件（例如 @Component、@Service、@Repository 等註解標註的類）。

4. 創建配置類來選擇 Printer 實現:Spring Boot 默認會使用 @Autowired 注入最符合的 Bean。如果有多個 Printer 實現類，你可以使用 @Qualifier 註解來選擇具體的實現。
```
@Configuration
public class AppConfig {

    @Bean
    public Printer hpPrinter() {
        return new HpPrinter();
    }

    @Bean
    public Printer canonPrinter() {
        return new CanonPrinter();
    }
}

```
或者直接選擇使用一個具體的實現：
```
@Component
public class Teacher {
    private Printer printer;

    // 通過 @Qualifier 指定具體的 Bean
    @Autowired
    public Teacher(@Qualifier("hpPrinter") Printer printer) {
        this.printer = printer;
    }

    public void teach(String lessonContent) {
        System.out.println("Teaching: " + lessonContent);
        printer.print(lessonContent);
    }
}
```
5. 啟動應用並測試:在 SpringBootIoCDemoApplication 類的 main 方法中，啟動應用並從 Spring 容器中獲取 Teacher 類的實例。
```
@SpringBootApplication
public class SpringBootIoCDemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringBootIoCDemoApplication.class, args);
        Teacher teacher = context.getBean(Teacher.class);
        teacher.teach("Understanding IoC in Spring Boot");
    }
}

```
解釋:啟動應用時，Spring 容器會自動創建 Teacher 和 Printer 這些 Bean，並將 Printer 注入到 Teacher 中。這樣你就可以看到 Teacher 類的 teach 方法被執行，並且會打印出 "Printing with HP Printer: Understanding IoC in Spring Boot"。
## IoC 優點
1. 解耦：IoC 可以幫助解耦應用中的各個組件，減少它們之間的依賴關係。這讓各個組件的測試變得更加簡單。
2. 提高靈活性：可以靈活地替換實現類，無需改變業務邏輯的代碼。
3. 提高可維護性：由於依賴關係由 Spring 管理，當需求變動時，只需要修改配置而不是程式碼。
## 總結
Spring Boot 的 IoC 機制通過依賴注入將對象的管理責任交給 Spring 容器，從而使應用更加靈活且易於維護。在實際開發中，我們只需要專注於業務邏輯，將具體的實現交給 Spring 管理。

# DI（Dependency Injection，依賴注入） 
DI（Dependency Injection，依賴注入） 是實現 IoC（控制反轉） 的一種方式，常用於降低對象之間的耦合度，從而提升系統的靈活性與可維護性。在 Spring 中，DI 使得對象的依賴關係不再由開發者手動設置，而是交由 Spring 容器自動注入。
## DI 實現方式
1. 建構子注入（Constructor Injection）
通過建構子傳遞依賴對象。這是最推薦的方式，能夠保證依賴在對象創建時就正確設置。
2. Setter 方法注入（Setter Injection）
通過提供 setter 方法設置依賴對象，對象可以在創建後進行設置。
3. 字段注入（Field Injection）
直接在類字段上使用 @Autowired 註解來自動注入依賴。

## 範例
假設我們有一個 Teacher 類需要依賴 Printer 類來打印教學內容。
1.  定義 Printer 類
```
@Component
public class Printer {
    public void print(String content) {
        System.out.println("Printing: " + content);
    }
}
```
2. 定義 Teacher 類，並注入 Printer
建構子注入：
```
@Component
public class Teacher {
    private final Printer printer;

    @Autowired
    public Teacher(Printer printer) {
        this.printer = printer;
    }

    public void teach(String lessonContent) {
        System.out.println("Teaching: " + lessonContent);
        printer.print(lessonContent);
    }
}
```
3. 啟動 Spring Boot 應用
```
@SpringBootApplication
public class SpringBootDiDemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringBootDiDemoApplication.class, args);
        Teacher teacher = context.getBean(Teacher.class);
        teacher.teach("Understanding Dependency Injection in Spring Boot");
    }
}
```
解釋：
1. Printer 類用 @Component 註解，讓 Spring 自動管理其生命周期。
2. Teacher 類依賴 Printer，並使用 建構子注入 (@Autowired) 將 Printer 實例注入到 Teacher 中。
3. 在 Spring Boot 應用啟動後，Teacher 實例會自動獲取 Printer 實例，並打印內容。

這樣，Spring 管理所有的依賴關係，減少了開發者的工作量，同時也使得代碼更具可維護性。
## DI 的好處
1. 解耦：降低對象間的耦合，增強靈活性。
2. 可測試性：易於替換依賴，便於單元測試。
3. 維護性：簡化對象管理，減少錯誤。

# AOP（Aspect-Oriented Programming） 
用來將跨越多個模塊的功能（如日誌、事務、安全）提取出來，從而讓業務邏輯更加專注並減少代碼重複。

## 核心概念
1. 切面（Aspect）：分離的功能模塊，如日誌或事務。
2. 通知（Advice）：具體的操作，如前置通知、後置通知、環繞通知。
3. 切點（Join Point）：方法執行的位置，AOP 會在這些位置執行通知。
4. 織入（Weaving）：將通知插入到目標對象的方法中。
## 常見通知類型：
1. 前置通知（@Before）：在方法執行之前執行。
2. 後置通知（@After）：在方法執行後執行。
3. 返回通知（@AfterReturning）：在方法成功執行後執行。
4. 異常通知（@AfterThrowing）：在方法拋出異常時執行。
5. 環繞通知（@Around）：包裹在方法執行前後，能控制是否執行目標方法。

## AOP 範例
1. 創建目標類 TeacherService
```
@Component
public class TeacherService {
    public void teach() {
        System.out.println("Teaching...");
    }
    
    public void assist() {
        System.out.println("Assisting...");
    }
}
```
2. 創建切面類 LoggingAspect 
```
@Aspect
@Component
public class LoggingAspect {

    // 前置通知：在目標方法執行前執行
    @Before("execution(* com.example.demo.TeacherService.teach(..))")
    public void logBeforeTeach() {
        System.out.println("Before Teaching...");
    }

    // 後置通知：在目標方法執行後執行
    @After("execution(* com.example.demo.TeacherService.teach(..))")
    public void logAfterTeach() {
        System.out.println("After Teaching...");
    }

    // 環繞通知：控制方法是否執行
    @Around("execution(* com.example.demo.TeacherService.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed();  // 執行目標方法
        System.out.println("After method: " + joinPoint.getSignature().getName());
        return result;
    }
}
```
3. 啟動 Spring Boot 應用
```
@SpringBootApplication
@EnableAspectJAutoProxy // 啟用 AOP
public class SpringAopApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);
        TeacherService teacherService = context.getBean(TeacherService.class);
        
        teacherService.teach();  // 呼叫目標方法
        teacherService.assist(); // 呼叫其他方法
    }
}
```

4. 預期輸出
```
Before method: teach
Before Teaching...
Teaching...
After Teaching...
After method: teach

Before method: assist
Assisting...
After method: assist

``` 
## AOP 的總結
1. AOP 用來分離橫切邏輯，使業務邏輯更加簡潔。
2. Spring AOP 通過代理模式實現，允許你在方法執行的不同階段插入額外的行為。
3. 常見應用包括日誌記錄、事務管理、安全控制等。


# 自動配置原理
用到檔案:springboot-register
## 自動配置
1. 遵循約定大約配置的原則，在boot程式啟動後，起步依賴中的一些bean物件會自動注入到ioc容器
3. 啟動自動設定：@EnableAutoConfiguration（包含在 @SpringBootApplication 中）開啟自動設定機制。
4. 讀取自動配置類別：Spring Boot 透過 META-INF/spring.factories 檔案中列出的自動配置類，逐一載入它們。
5. 條件註解控制載入：自動設定類別中大量使用 @Conditional 系列註解（如 @ConditionalOnClass、@ConditionalOnProperty），根據條件決定是否載入某些設定。
6. 結合外部設定檔：自動設定的 Bean 屬性可以從外部設定檔（如 application.properties）中讀取，允許開發者覆蓋預設配置。
7. 支援排除特定自動配置：可以透過 exclude 屬性或設定檔排除不需要的自動配置類別。

總結：Spring Boot 自動配置透過條件註解和設定檔簡化了應用程式配置，自動載入符合條件的 Bean，開發者只需在必要時進行調整或覆蓋。
## 自動配置-源碼分析
程式引入spring-boot-starter-web 起步依賴，啟動後，會自動往ioc容器中註入DispatcherServlet
![螢幕擷取畫面 2024-11-11 232629](https://hackmd.io/_uploads/Byl5foJzJx.png)
![螢幕擷取畫面 2024-11-11 232900](https://hackmd.io/_uploads/BkbkQiyGkx.png)
# 自定義starter
用到文件:springboot-auto-config (2.0 jar版本在資料裡面的自動配置)![螢幕擷取畫面 2024-11-12 132815](https://hackmd.io/_uploads/Sk1EMdeGyg.png)


1. 碰到狀況:在實際開發中，常常會定義一些公共元件，提供給各個專案團隊使用。而在SpringBoot的專案中，一般會將這些公共元件封裝為SpringBoot 的 starter。
2. 需求:自訂mybatis的starter
3. 步驟:建立 dmybatis-spring-boot-autoconfigure 模組，提供自動設定功能，並自訂設定檔 META-INF/spring/xxx.imports
4. 發布自訂 Starter:將 Starter 專案打包發佈到 Maven 倉庫（本地或遠端）。其他項目只需在 pom.xml 中添加依賴，即可引入該 Starter 並獲得相應的自動配置。
5. 在其他項目中使用自訂 Starter:在實際項目的 pom.xml 中加入自訂 Starter 的依賴關係，根據需要在 application.properties 檔案中設定 Starter 提供的相關屬性。 Starter 中的自動配置類別會根據這些屬性來決定如何初始化 Bean。
用到的資源:bilibili的黑馬程序員的影片整理的重點