在 Spring 中,IoC 注入可以通過多種方式實現,涵蓋不同場景的依賴管理。以下是 8 種常見場景的詳細示例及說明,結合 XML、注解和 Java 配置類三種方式。
1. 構造器注入(推薦方式)
通過構造器傳遞依賴,確保對象不可變且依賴完整。
注解方式:
@Service
public class OrderService {private final PaymentService paymentService;@Autowired // Spring 4.3+ 可省略(單構造器時)public OrderService(PaymentService paymentService) {this.paymentService = paymentService;}
}@Component
public class PaymentService {public void process() {System.out.println("Processing payment...");}
}
XML 方式:
<bean id="paymentService" class="com.example.PaymentService"/><bean id="orderService" class="com.example.OrderService"><constructor-arg ref="paymentService"/>
</bean>
Java 配置類:
@Configuration
public class AppConfig {@Beanpublic PaymentService paymentService() {return new PaymentService();}@Beanpublic OrderService orderService() {return new OrderService(paymentService());}
}
2. Setter 注入
通過 Setter 方法注入依賴,適合可選依賴或需要動態變更的場景。
注解方式:
@Service
public class ProductService {private InventoryService inventoryService;@Autowiredpublic void setInventoryService(InventoryService inventoryService) {this.inventoryService = inventoryService;}
}@Component
public class InventoryService {public void checkStock() {System.out.println("Checking stock...");}
}
XML 方式:
<bean id="inventoryService" class="com.example.InventoryService"/><bean id="productService" class="com.example.ProductService"><property name="inventoryService" ref="inventoryService"/>
</bean>
3. 字段注入(不推薦,但常見)
直接通過字段注入依賴,簡潔但隱藏依賴關系。
@Service
public class CartService {@Autowiredprivate DiscountService discountService;
}@Component
public class DiscountService {public void applyDiscount() {System.out.println("Applying discount...");}
}
4. 集合注入(多個同類型 Bean)
注入多個相同接口/父類的實現類。
示例代碼:
public interface NotificationSender {void send(String message);
}@Component
public class EmailSender implements NotificationSender {@Overridepublic void send(String message) {System.out.println("Email: " + message);}
}@Component
public class SmsSender implements NotificationSender {@Overridepublic void send(String message) {System.out.println("SMS: " + message);}
}
注入方式:
@Service
public class NotificationService {private final List<NotificationSender> senders;@Autowiredpublic NotificationService(List<NotificationSender> senders) {this.senders = senders;}public void broadcast(String message) {senders.forEach(sender -> sender.send(message));}
}
5. 條件注入(@Qualifier 或 @Primary)
解決多個同類型 Bean 的歧義性問題。
使用 @Qualifier
:
public interface DataSource {void connect();
}@Component("mysqlDataSource")
public class MySqlDataSource implements DataSource {@Overridepublic void connect() {System.out.println("MySQL connected");}
}@Component("oracleDataSource")
public class OracleDataSource implements DataSource {@Overridepublic void connect() {System.out.println("Oracle connected");}
}@Service
public class ReportService {private final DataSource dataSource;@Autowiredpublic ReportService(@Qualifier("mysqlDataSource") DataSource dataSource) {this.dataSource = dataSource;}
}
使用 @Primary
:
@Configuration
public class AppConfig {@Bean@Primary // 默認優先選擇public DataSource mysqlDataSource() {return new MySqlDataSource();}@Beanpublic DataSource oracleDataSource() {return new OracleDataSource();}
}
6. 外部化配置注入(@Value)
注入配置文件(如 application.properties
)中的值。
配置文件:
app.name=MySpringApp
app.max.connections=10
注入方式:
@Component
public class AppConfigBean {@Value("${app.name}")private String appName;@Value("${app.max.connections}")private int maxConnections;// Getter/Setter
}
7. 循環依賴處理
構造器注入無法解決循環依賴,需改用 Setter 注入。
示例代碼:
@Service
public class ServiceA {private ServiceB serviceB;@Autowiredpublic void setServiceB(ServiceB serviceB) {this.serviceB = serviceB;}
}@Service
public class ServiceB {private ServiceA serviceA;@Autowiredpublic void setServiceA(ServiceA serviceA) {this.serviceA = serviceA;}
}
8. 條件化 Bean 注入(@Profile 或 @Conditional)
根據環境或條件動態選擇 Bean。
使用 @Profile
:
@Configuration
public class DataSourceConfig {@Bean@Profile("dev")public DataSource devDataSource() {return new H2DataSource();}@Bean@Profile("prod")public DataSource prodDataSource() {return new MySqlDataSource();}
}
使用 @Conditional
:
public class MongoDBCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return "true".equals(context.getEnvironment().getProperty("enable.mongo"));}
}@Configuration
public class DatabaseConfig {@Bean@Conditional(MongoDBCondition.class)public DataSource mongoDataSource() {return new MongoDataSource();}
}
總結
- 構造器注入:推薦用于強制依賴,確保對象不可變。
- Setter/字段注入:適合可選依賴或需要靈活性的場景。
- 集合注入:處理多實現類的統一管理。
- 條件注入:通過
@Qualifier
、@Primary
或@Profile
解決歧義。 - 循環依賴:優先通過設計避免,或改用 Setter 注入。
- 外部化配置:結合
@Value
動態注入屬性值。 - 條件化 Bean:根據環境或業務規則動態裝配。
具體選擇取決于項目需求,Spring Boot 進一步簡化了配置(如自動配置、@ConditionalOnProperty
)。