跟在Spring XML文件中使用`<import>`元素添加模块化的配置类似,@Import注解允许你加载其他配置类中的@Bean定义:
```java
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
```
现在,当实例化上下文时,你只需要显式的指定ConfigB,而不需要既提供ConfigA.class,又提供ConfigB.class:
```java
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
```
这种方式简化了容器的初始化,因为只需要处理一个类,而不是让开发者记住构造期间的大量@Configuration类。
幸运的是,解决该问题是很容易的。正如我们[已经讨论](http://docs.spring.io/spring/docs/4.2.0.RC1/spring-framework-reference/htmlsingle/#beans-java-dependencies)的,@Bean可以有任意多个用来描述bean依赖的参数。让我们探讨一个更现实的场景,在这里将使用一些彼此依赖的@Configuration类:
```java
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
```
这里有另外的方法可以达到相同的效果。记住,@Configuration根本上只是容器中的另一个bean-这意味着它们可以像其他bean那样充分利用@Autowired注入元数据。
**注:** 确保以这种方式注入的都是简单类型的。@Configuration类在容器初始化时被处理的相当早,用这种方式强制注入依赖可能导致无法预料地过早初始化问题。只要有可能就采用上面示例中基于参数的注入方式。
```java
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService() {
// navigate ‘through‘ the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
```
在上面的解决方案中,我们可以很明确地知道AccountRepository定义的地方。然而,ServiceConfig现在紧紧地跟RepositoryConfig耦合了。这就是权衡。紧耦合在某种程度上可以通过使用基于接口或抽象类的@Configuration类来减轻。考虑以下内容:
```java
@Configuration
public class ServiceConfig {
Condition接口的实现者只需简单地提供一个返回true或false的`matches(…?)`方法。例如,下面是@Profile注解采用的Condition实现:
```java
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
// Read the @Profile annotation attributes
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
```
具体参考[@Conditional javadocs](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html)。
* 结合Java和XML配置
Spring @Configuration类支持目的不是想要100%的替换Spring XML。一些设施,比如Spring XML命名空间仍旧是配置容器的完美方式。在XML很方便或必须的情况下,你有个选择:采用"XML为中心"的方式实例化容器,比如ClassPathXmlApplicationContext,或使用AnnotationConfigApplicationContext以"Java为中心"的方式,并使用@ImportResource注解导入需要的XML。
记着@Configuration类本质上只是容器中的bean定义。在下面的示例中,我们创建了一个名称为AppConfig的@Configuration类,并将它作为`<bean/>`定义包含到`system-test-config.xml`中。因为`<context:annotation-config/>`是开启的,容器将会识别@Configuration注解,并正确地处理AppConfig中声明的@Bean方法。
```java
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
```
system-test-config.xml如下:
```xml
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
system-test-config.xml如下:
```xml
<beans>
<!-- picks up and registers AppConfig as a bean definition -->
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
在将@Configuration类作为配置容器的主要机制的应用中,仍旧存在对XML的需求。在那些场景中,可以使用@ImportResource,并定义所需的XML。这样做可以实现以"Java为中心"的方式配置容器,并保留最低限度的XML。
```java
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {