自 Spring 3.0 起,框架全面推廣「無 XML」配置風格,主要分為基於 Java 的配置與基於註解的組件掃描兩大流派。本文深入對比兩者的核心註解、適用場景、優缺點與最佳實踐,幫助開發者在專案中做出合理的架構選型。
| 分類 | 註解 | 作用 | 等價 XML |
|---|---|---|---|
| 註解驅動 (組件掃描) | @Component | 通用組件標識 | <bean> |
@Service | 業務邏輯層 | <bean> | |
@Repository | 資料存取層(含異常轉換) | <bean> | |
@Controller | 控制層 | <bean> | |
| Java Config (程式化配置) | @Configuration | 聲明配置類(相當於 spring.xml) | <beans> |
@Bean | 方法返回值註冊為 Bean | <bean id="..." class="..."/> | |
@ComponentScan | 啟用組件掃描(可配合註解驅動) | <context:component-scan/> |
package di;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service // 1. 標識為 Spring Bean
public class UseFunctionService {
@Autowired // 2. 自動注入依賴
private FunctionService functionService;
public String sayHello(String word) {
return functionService.toHello(word);
}
}
@Configuration
@ComponentScan("di") // 掃描 di 包下所有 @Component 衍生註解
public class AppConfig { }
package javaconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 1. 標識為配置類
public class JavaConfig {
@Bean // 2. 方法返回值註冊為 Bean,id 默認為方法名
public UseFunctionService useFunctionService() {
return new UseFunctionService(); // 手動 new,可控制構造邏輯
}
@Bean
public FunctionService functionService() {
return new FunctionServiceImpl();
}
}
| 維度 | 註解驅動 (@ComponentScan) | Java Config (@Configuration + @Bean) |
|---|---|---|
| Bean 定義位置 | 分散在各業務類上 | 集中在配置類方法中 |
| 業務類耦合度 | 強(依賴 Spring 註解) | 零(純 POJO) |
| 第三方庫整合 | 困難(無法修改源碼加註解) | 極佳(寫 @Bean 方法包裝) |
| 條件裝配 | 需配合 @Conditional 於類上 | 靈活(方法內任意邏輯判斷) |
| 依賴關係可視化 | 較差(需 IDE 支援) | 優秀(配置類即文檔) |
| 啟動掃描開銷 | 類路徑掃描較慢 | 無掃描,啟動更快 |
業界公認的 Spring 專案結構建議:
@Configuration
@EnableTransactionManagement
public class DataConfig {
@Bean
public DataSource dataSource() { ... }
@Bean
public PlatformTransactionManager txManager() { ... }
}
@Service
public class OrderService {
@Autowired private OrderRepository repo;
@Autowired private PaymentClient payClient;
// 業務邏輯...
}
@Configuration
@ComponentScan(basePackages = "com.myapp.service, com.myapp.repository, com.myapp.controller")
@Import({DataConfig.class, WebConfig.class, SecurityConfig.class})
public class ApplicationConfig { }
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
// dataSource 由容器自動注入
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
return emf;
}
@Bean
@ConditionalOnProperty(name = "cache.type", havingValue = "redis")
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
return new RedisCacheManager(factory);
}
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager simpleCacheManager() {
return new SimpleCacheManager();
}
Spring 5.2+ 支援,關閉 CGLIB 代理,提升啟動速度,適用於不需 `@Bean` 方法互調的配置類。
Spring 配置沒有絕對優劣,關鍵在於分層選型:
@Import、@ComponentScan 無縫協作,構建出既靈活又治理良好的 Spring 應用。現代 Spring Boot 專案更進一步:@SpringBootApplication 組合了 `@Configuration` + `@EnableAutoConfiguration` + `@ComponentScan`,將上述最佳實踐內化為預設約定,開發者只需專注於業務邏輯與少量自定義配置。