<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 核心:啟用註解處理器 -->
<context:annotation-config/>
<!-- 掃描組件 (替代 XML 定義 bean) -->
<context:component-scan base-package="com.example.service, com.example.dao"/>
</beans>
關鍵區別:annotation-config 僅註冊後置處理器 (處理 @Autowired 等);component-scan 包含前者並額外掃描 @Component 類自動註冊 Bean。
@Service
public class UserService {
// 構造器注入 (推薦,不可變、可測試)
private final UserRepository userRepository;
@Autowired // 單構造器可省略
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Setter 注入 (可選依賴)
private CacheManager cacheManager;
@Autowired(required = false)
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
// 字段注入 (不推薦,難測試、循環依賴隱患)
@Autowired
private EmailService emailService;
}
required=true,找不到 Bean 拋異常NoUniqueBeanDefinitionException,需配合 @Qualifier@Autowired
@Qualifier("redisCacheManager") // 指定 Bean 名稱
private CacheManager cacheManager;
// 或在目標 Bean 上標識
@Primary // 優先選擇
@Bean
public CacheManager redisCacheManager() { ... }
@Bean
public CacheManager ehCacheManager() { ... }
@Resource(name = "userRepository") // 指定名稱
private UserRepository userRepository;
// 未指定 name 時:優先按字段名匹配 Bean 名稱,退回到類型匹配
@Resource
private UserRepository userRepository; // 查找名為 "userRepository" 的 Bean
| 特性 | @Autowired | @Resource |
|---|---|---|
| 來源 | Spring 專有 | JSR-250 (Java EE 標準) |
| 匹配策略 | 按類型 → @Qualifier 按名 | 按名稱 (@Resource.name/字段名) → 類型 |
| 構造器注入 | 支援 | 不支援 (僅字段/Setter) |
| 可選依賴 | @Autowired(required=false) | 無直接對應 |
@Inject
private UserRepository userRepository; // 需引入 javax.inject:javax.inject:1
行為與 @Autowired 一致,但無 required 屬性,配合 @Named 指定名稱。
| 註解 | 用途 | 層級約定 |
|---|---|---|
| @Component | 通用組件 | 無特定層 |
| @Repository | DAO 層 | 自動轉換數據訪問異常 (PersistenceExceptionTranslationPostProcessor) |
| @Service | 業務邏輯層 | 語義標識,可切面增強事務 |
| @Controller | Web 控制器 | Spring MVC 識別,請求映射 |
| @Configuration | 配置類 | 替代 XML,定義 @Bean 方法 |
| @Bean | 方法級 Bean 定義 | 方法名 = Bean 名,返回值 = Bean 實例 |
@Configuration
@ComponentScan(basePackages = "com.example",
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Controller.class))
public class AppConfig {
@Bean
@Primary
public DataSource dataSource() { ... }
@Bean
@Scope("prototype")
public MyPrototypeBean prototypeBean() { return new MyPrototypeBean(); }
}
@Component
public class LifecycleBean implements InitializingBean, DisposableBean {
@PostConstruct // JSR-250,初始化後調用 (推薦)
public void init() { System.out.println("@PostConstruct init"); }
@PreDestroy // JSR-250,銷毀前調用 (推薦)
public void destroy() { System.out.println("@PreDestroy destroy"); }
// InitializingBean 接口方法
@Override
public void afterPropertiesSet() { System.out.println("afterPropertiesSet"); }
// DisposableBean 接口方法
@Override
public void destroy() { System.out.println("DisposableBean.destroy"); }
}
// XML 配置方式 (無侵入代碼)
<bean id="xmlBean" class="com.example.XmlBean"
init-method="customInit" destroy-method="customDestroy"/>
@PostConstruct / afterPropertiesSet() / init-method@PreDestroy / destroy() / destroy-method@Bean
@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
public CacheManager cacheManager() { ... }
@Bean
@ConditionalOnMissingBean(RedisTemplate.class)
public RedisTemplate fallbackRedisTemplate() { ... }
@Bean
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
public RedisCacheManager redisCacheManager() { ... }
@Configuration
@Import({MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class DynamicConfig { }
// 實現 ImportSelector:根據條件返回配置類名數組
// 實現 ImportBeanDefinitionRegistrar:程序化註冊 BeanDefinition
註解配置讓 Spring 從「XML 地獄」解脫,但濫用會導致隱式依賴難追踪。原則:業務代碼用註解,基礎設施用配置類,測試用 XML/動態註冊覆蓋。