本章彙整 Spring 開發中最常見的錯誤類型,幫助你快速排查問題。


Bean 定義常見錯誤#

錯誤 1:Bean 掃描不到#

症狀啟動時報錯找不到 Bean
原因Bean 不在 @ComponentScan 掃描範圍內
解決明確指定掃描路徑或調整套件結構
// 預設只掃描啟動類所在套件及子套件
@SpringBootApplication
@ComponentScan("com.example") // 明確指定
public class Application { }

錯誤 2:構造器參數找不到對應 Bean#

症狀Parameter 0 of constructor required a bean that could not be found
原因顯式定義構造器後,Spring 會嘗試自動裝配參數
解決確保參數類型有對應的 Bean,或使用 @Autowired(required=false)
@Service
public class ServiceImpl {
    // Spring 會嘗試找一個 String 類型的 Bean 來注入
    public ServiceImpl(String serviceName) {
        this.serviceName = serviceName;
    }
}

// 解決:提供對應的 Bean
@Bean
public String serviceName() {
    return "MyServiceName";
}

錯誤 3:存在多個相同類型的 Bean#

症狀required a single bean, but 2 were found
原因有多個候選 Bean,Spring 無法決定使用哪個
解決使用 @Primary@Qualifier 或變數名匹配
// 方案 1:標記 @Primary
@Repository
@Primary
public class OracleDataService implements DataService { }

// 方案 2:使用 @Qualifier
@Autowired
@Qualifier("oracleDataService")
private DataService dataService;

// 方案 3:變數名與 Bean 名一致
@Autowired
private DataService oracleDataService;

依賴注入失敗場景#

錯誤 4:Bean 名稱首字母大小寫問題#

症狀No qualifying bean of type … available
原因@Qualifier 指定的名稱大小寫錯誤
規則一般首字母小寫,連續大寫開頭則保持原樣
// CassandraDataService -> cassandraDataService
// SQLiteDataService -> SQLiteDataService (連續大寫)

@Autowired
@Qualifier("cassandraDataService") // 注意首字母小寫
private DataService dataService;

錯誤 5:內部類 Bean 引用#

症狀找不到內部類 Bean
原因內部類 Bean 名稱包含外部類名
格式外部類名.內部類名(外部類首字母小寫)
public class StudentController {
    @Repository
    public static class InnerClassDataService implements DataService { }
}

// 引用方式
@Autowired
@Qualifier("studentController.InnerClassDataService")
private DataService innerClassDataService;

錯誤 6:@Value 注入非預期值#

症狀@Value 注入的值不是組態檔案中的值
原因與系統環境變數或系統屬性同名
解決使用唯一的屬性名稱
# application.properties
# 錯誤:username 可能與系統環境變數衝突
username=admin

# 正確:使用有前綴的唯一名稱
app.username=admin

AOP 不生效的原因#

錯誤 7:this 呼叫導致 AOP 失效#

症狀切面不執行
原因this 是原始物件,不是代理物件
解決自己注入自己或使用 AopContext.currentProxy()
@Service
public class MyService {
    @Autowired
    private MyService self; // 注入代理物件

    public void methodA() {
        self.methodB(); // 通過代理呼叫
    }
}

錯誤 8:方法不是 public#

症狀切面不執行
原因Spring AOP 只能代理 public 方法
解決確保被切面的方法是 public

錯誤 9:final 類或方法#

症狀CGLIB 代理失敗
原因final 類無法被繼承,final 方法無法被覆寫
解決移除 final 修飾符

事務不回滾的情況#

錯誤 10:例外被 catch 吞掉#

症狀發生例外但事務沒有回滾
原因例外被捕獲後沒有重新拋出
解決重新拋出例外或手動設定回滾
@Transactional
public void createOrder() {
    try {
        orderDao.insert(order);
    } catch (Exception e) {
        log.error("失敗", e);
        // 方案 1:重新拋出
        throw e;
        // 方案 2:手動標記回滾
        // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

錯誤 11:拋出的是 Checked Exception#

症狀拋出 Exception 但沒有回滾
原因預設只回滾 RuntimeException 和 Error
解決指定回滾的例外類型
@Transactional(rollbackFor = Exception.class)
public void createOrder() throws Exception { }

錯誤 12:事務方法被 this 呼叫#

症狀事務不生效
原因與 AOP 失效原因相同
解決確保通過代理物件呼叫事務方法

Spring Boot 自動組態問題#

錯誤 13:自動組態未生效#

症狀預期的自動組態沒有載入
可能原因缺少依賴、條件不滿足、被排除
排查使用 --debug 啟動查看自動組態報告
java -jar myapp.jar --debug
# 或在 application.properties 中:
# debug=true

錯誤 14:條件化組態不生效#

註解條件
@ConditionalOnClass類路徑中存在指定類
@ConditionalOnMissingBean容器中不存在指定 Bean
@ConditionalOnProperty組態屬性滿足條件
@Configuration
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class FeatureConfig { }

組態屬性綁定問題#

錯誤 15:@ConfigurationProperties 不生效#

症狀屬性沒有注入到組態類
原因缺少 @EnableConfigurationProperties@ConfigurationPropertiesScan
解決在啟動類上添加相應註解
@ConfigurationProperties(prefix = "myapp")
@Data
public class MyAppProperties {
    private String name;
}

// 啟用組態屬性
@SpringBootApplication
@EnableConfigurationProperties(MyAppProperties.class)
public class Application { }

錯誤 16:屬性名稱綁定失敗#

症狀屬性綁定時報錯或為 null
原因屬性名稱格式不匹配
規則支援多種格式:camelCase、kebab-case、snake_case
# 以下格式都可以綁定到 firstName 屬性
myapp.first-name=John
myapp.firstName=John
myapp.first_name=John

快速排錯指南#

啟動時報錯#

┌─────────────────────────────────────────────────────────────┐
│                    Bean 相關啟動錯誤                          │
├─────────────────────────────────────────────────────────────┤
│ "No qualifying bean" → 檢查 @ComponentScan、包結構          │
│ "required a single bean, but N were found" → 用 @Qualifier  │
│ "Error creating bean" → 檢查依賴、構造器參數                 │
│ "BeanCurrentlyInCreationException" → 循環依賴               │
└─────────────────────────────────────────────────────────────┘

執行時問題#

┌─────────────────────────────────────────────────────────────┐
│                    執行時常見問題                             │
├─────────────────────────────────────────────────────────────┤
│ AOP 不生效 → 檢查是否 this 呼叫、是否 public 方法           │
│ 事務不回滾 → 檢查例外類型、是否被 catch                     │
│ 請求 404 → 檢查 URL 映射、掃描範圍                          │
│ 請求 400 → 檢查參數類型、格式、驗證                         │
│ 請求 500 → 查看例外堆疊                                     │
└─────────────────────────────────────────────────────────────┘

除錯技巧#

1. 啟用詳細日誌#

# application.yml
logging:
  level:
    org.springframework: DEBUG
    org.springframework.web: DEBUG
    org.springframework.transaction: TRACE

2. 檢查 Bean 是否存在#

@Autowired
private ApplicationContext context;

public void checkBeans() {
    // 列出所有 Bean
    String[] beanNames = context.getBeanDefinitionNames();

    // 檢查特定類型的 Bean
    Map<String, DataService> beans = context.getBeansOfType(DataService.class);
}

3. 檢查是否為代理物件#

@Autowired
private MyService myService;

public void checkProxy() {
    System.out.println("Is proxy: " + AopUtils.isAopProxy(myService));
    System.out.println("Is CGLIB proxy: " + AopUtils.isCglibProxy(myService));
    System.out.println("Is JDK proxy: " + AopUtils.isJdkDynamicProxy(myService));
}

4. 查看自動組態報告#

CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------
   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes ...

Negative matches:
-----------------
   ActiveMQAutoConfiguration:
      - @ConditionalOnClass did not find required class ...

總結:防錯 Checklist#

  • Bean 是否在掃描範圍內
  • 依賴是否正確注入(檢查 null)
  • 是否通過代理物件呼叫(AOP/事務)
  • 方法是否為 public(AOP 要求)
  • 例外是否正確拋出(事務要求)
  • 組態屬性名稱是否正確
  • 依賴版本是否相容
  • 是否有多個候選 Bean