Spring 的 @Configuration 类支持的目的并不是要 100% 完全取代 Spring XML。一些设施,如 Spring XML 命名空间,仍然是配置容器的理想方式。在 XML 方便或必要的情况下,你有一个选择:要么通过使用例如 ClassPathXmlApplicationContext以 「以 XML 为中心 」的方式实例化容器,要么通过使用AnnotationConfigApplicationContext 和 @ImportResource 注解来根据需要导入 XML,以 「以 Java 为中心」 的方式实例化它。

以 XML 为中心的 @Configuration 类的使用

从 XML 引导 Spring 容器并以临时的方式包含 @Configuration 类可能是更好的做法。例如,在一个使用Spring XML 的大型现有代码库中,根据需要创建 @Configuration 类并从现有的 XML 文件中包含它们是比较容易的。在本节后面,我们将介绍在这种 「以XML为中心 」的情况下使用 @Configuration 类的选项。

将 @Configuration 类声明为普通的 Spring 元素

记住,@Configuration 类最终是容器中的 Bean 定义。在这个系列的例子中,我们创建了一个名为 AppConfig 的 @Configuration 类,并将其作为 <bean/> 定义包含在 system-test-config.xml 中。因为 <context:annotation-config/> 被打开了,所以容器会识别 @Configuration 注解并正确处理 AppConfig 中声明的 @Bean 方法。

下面的例子展示了 Java 中的一个普通配置类:

  1. @Configuration
  2. public class AppConfig {
  3. @Autowired
  4. private DataSource dataSource;
  5. @Bean
  6. public AccountRepository accountRepository() {
  7. return new JdbcAccountRepository(dataSource);
  8. }
  9. @Bean
  10. public TransferService transferService() {
  11. return new TransferService(accountRepository());
  12. }
  13. }

下面的示例显示了一个样例 system-test-config.xml 文件的一部分:

  1. <beans>
  2. <!-- 启用注解助理 @Autowired and @Configuration -->
  3. <context:annotation-config/>
  4. <!-- 加载配置文件 -->
  5. <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
  6. <bean class="com.acme.AppConfig"/>
  7. <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  8. <property name="url" value="${jdbc.url}"/>
  9. <property name="username" value="${jdbc.username}"/>
  10. <property name="password" value="${jdbc.password}"/>
  11. </bean>
  12. </beans>

下面的示例显示了一个可能的 jdbc.properties 文件:

  1. jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
  2. jdbc.username=sa
  3. jdbc.password=
  1. public static void main(String[] args) {
  2. ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
  3. TransferService transferService = ctx.getBean(TransferService.class);
  4. // ...
  5. }

:::info 在 system-test-config.xml 文件中,AppConfig <bean/> 并没有声明一个 id 元素。虽然这样做是可以接受的,但考虑到没有其他 Bean 会引用它,而且它也不可能被明确地从容器中获取名字,所以这样做是不必要的。同样地,DataSource Bean 只按类型自动连接,所以严格来说不需要明确的 bean id。 :::

使用 <context:component-scan/>来拾取 @Configuration 类

因为 @Configuration 是用 @Component 元注解的,所以 @Configuration 注解的类自动成为组件扫描的候选对象。使用与前面例子中描述的相同场景,我们可以重新定义 system-test-config.xml 以利用组件扫描。注意,在这种情况下,我们不需要明确声明 <context:annotation-config/>,因为<context:component-scan/>可以实现同样的功能。

下面的示例显示了修改后的 system-test-config.xml 文件:

  1. <beans>
  2. <!-- picks up and registers AppConfig as a bean definition -->
  3. <context:component-scan base-package="com.acme"/>
  4. <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
  5. <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  6. <property name="url" value="${jdbc.url}"/>
  7. <property name="username" value="${jdbc.username}"/>
  8. <property name="password" value="${jdbc.password}"/>
  9. </bean>
  10. </beans>

**@Configuration** 类为中心使用 XML 与 @ImportResource

在 @Configuration 类是配置容器的主要机制的应用中,仍然可能需要至少使用一些 XML。在这些情况下,你可以使用 @ImportResource 并只定义你需要的 XML。这样做实现了 「以 Java 为中心 」的配置容器的方法,并使 XML 保持在最低限度。下面的例子(包括一个配置类、一个定义 bean 的 XML 文件、一个属性文件和主类)显示了如何使用 @ImportResource 注解来实现 「以 Java 为中心 」的配置,并根据需要使用 XML。

  1. @Configuration
  2. @ImportResource("classpath:/com/acme/properties-config.xml") // 导入了 xml 配置
  3. public class AppConfig {
  4. @Value("${jdbc.url}")
  5. private String url;
  6. @Value("${jdbc.username}")
  7. private String username;
  8. @Value("${jdbc.password}")
  9. private String password;
  10. @Bean
  11. public DataSource dataSource() {
  12. return new DriverManagerDataSource(url, username, password);
  13. }
  14. }

properties-config.xml

  1. <beans>
  2. <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
  3. </beans>
  1. jdbc.properties
  2. jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
  3. jdbc.username=sa
  4. jdbc.password=
  1. public static void main(String[] args) {
  2. ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  3. TransferService transferService = ctx.getBean(TransferService.class);
  4. // ...
  5. }