Spring Java Configuration - M3 中的新特性

工程 | Chris Beams | 2008 年 3 月 27 日 | ...

今天标志着 Spring Java Configuration 项目(简称 JavaConfig)的第三个里程碑版本发布。该版本包含了大量的错误修复和新特性 - 我将在下面重点介绍一些最有趣的更改,但首先让我快速回顾一下 JavaConfig 的核心是什么。

如果您对 Spring 有任何经验,下面的 XML 配置片段可能会很熟悉。我们假设正在查看一个名为 application-config.xml 的文件


<beans>
	<bean id="orderService" class="com.acme.OrderService"/>
		<constructor-arg ref="orderRepository"/>
	</bean>
	<bean id="orderRepository" class="com.acme.OrderRepository"/>
		<constructor-arg ref="dataSource"/>
	</bean>
</beans>

当然,这个 XML 配置最终将作为 Spring ApplicationContext 实例化和配置我们的 bean 的一组指令


ApplicationContext ctx = new ClassPathXmlApplicationContext("application-config.xml");
OrderService orderService = (OrderService) ctx.getBean("orderService");

JavaConfig 提供了一种另一种配置 Spring IoC 容器的机制,这一次完全使用 Java 而不是需要 XML 来完成工作。让我们将上面的配置迁移到 JavaConfig


@Configuration
public class ApplicationConfig {
	public @Bean OrderService orderService() {
		return new OrderService(orderRepository());
	}

	public @Bean OrderRepository orderRepository() {
		return new OrderRepository(dataSource());
	}

	public @Bean DataSource dataSource() {
		// instantiate and return an new DataSource ...
	}
}

与原始的 XML 文件一样,这个类只是一组关于如何构建应用程序各种组件的指令。我们将把这些指令提供给一个专门设计用于读取和执行基于 Java 的配置指令的 ApplicationContext 实现


JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class);
OrderService orderService = ctx.getBean(OrderService.class);

就是这样!嗯,差不多吧。当然 JavaConfig 还有更多内容,但在很大程度上,其功能集与 Spring XML 配置中可用的功能集是 1:1 的。有关如何使用 JavaConfig 的完整详细信息,请参阅参考文档。如果您是 JavaConfig 的新手,请务必查看快速入门部分。

无论如何,JavaConfig 的好处显而易见

  • 它是纯 Java 的,因此无需 XML
  • 您可以在配置代码中获得面向对象的所有好处
  • 它是类型安全的,并且易于重构
  • 您仍然可以获得核心 Spring IoC 容器的全部强大功能

考虑到这些,让我们来看看 M3 版本中有哪些变化

AnnotationApplicationContext 已弃用
这很难算是一个“新特性”,但提及这个变化很重要,因为我下面讨论的很多内容都围绕着 JavaConfigApplicationContext,它是 AnnotationApplicationContext 的后继者。为什么进行这项更改? AnnotationApplicationContext 与 Spring 2.5 的注解驱动注入(Annotation-Driven Injection)功能存在严重的命名冲突。JavaConfig 提供了与注解驱动注入不同的配置方法,因此我们希望通过完全重命名该类来明确区分。 AnnotationApplicationContext 将一直保持弃用状态,直到 1.0.0.rc1 版本发布,届时将永久删除。
类型安全方面的改进
虽然上面提到的 JavaConfigApplicationContext 在很大程度上与其前身行为相似,但它也引入了类型安全的 getBean() 方法,充分利用了泛型。下面的代码现在可以工作(并且从现在开始是使用 JavaConfig 的首选方法)

JavaConfigApplicationContext context = new JavaConfigApplicationContext(AppConfig.class);
OrderService orderService = context.getBean(OrderService.class);

看啊,无需类型转换!也不需要基于字符串查找。当然,这会引出一个问题:“如果上下文中有两个或多个 OrderService 类型的对象,该怎么办?” 这种情况很容易发生,并且有多种方法可以解决。为了在本篇博文中简洁起见,我只简单地建议感兴趣的读者查阅参考文档中的消歧选项部分。

这些类型安全的 getBean() 方法也被添加到 ConfigurationSupport 基类中,因此可以实现如下操作


@Configuration
public class ApplicationConfig extends ConfigurationSupport {
	public @Bean OrderRepository orderRepository() {
		return new JdbcOrderRepository(this.getBean(DataSource.class));
	}
}
重要的文档更新
我们努力将 JavaConfig 的文档质量提升到 Spring 素以闻名的水平。如上链接所示,参考文档提供 HTMLPDF 格式。请注意,这些文档也作为常规 zip 发行版的一部分打包,可通过 SourceForge 获取。与 M3 中的所有新增内容一样,您对文档的反馈将有助于在项目迈向 1.0 GA 版本时改进它。
Web 层对 JavaConfig 的支持
在此版本之前,JavaConfig 必须通过 XML 进行“引导”,才能与 Spring 的 ContextLoaderListenerDispatcherServlet 类一起使用。为了解决这一限制,增加了 JavaConfigWebApplicationContext。只需在 web.xml 中将此类指定为 contextClass 参数,即可直接使用您的 @Configuration

<web-app>
    <!-- Configure ContextLoaderListener to use JavaConfigWebApplicationContext
         instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.config.java.context.JavaConfigWebApplicationContext</param-value>
    </context-param>
    <!-- Configuration locations must consist of one or more comma- or space-delimited
         fully-qualified @Configuration classes -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>example.RootApplicationConfig</param-value>
    </context-param>
    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher-servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use JavaConfigWebApplicationContext
             instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.config.java.context.JavaConfigWebApplicationContext</param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
             and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>example.web.WebBeansConfig</param-value>
        </init-param>
    </servlet>
</web-app>

实际上,很可能许多人会继续使用 XML 和 JavaConfig 的组合,尤其是在 Web 应用程序中。这种方法仍然工作良好(参见组合配置方法),但对团队而言,重要的是如果用户需要,我们能够提供一种真正的“无 XML”方法。此更改完善了这种可能性。

通过新的 @Import 注解改进模块化
就像 Spring XML 配置的 <import/> 元素一样,现在可以有一个 @Configuration 类导入另一个类(以及它的所有 bean 定义)

@Configuration
public class FooConfig {
	public @Bean Foo foo() { ... }
	public @Bean Bar bar() { ... }
}

@Import(FooConfig.class)
@Configuration
public class ApplicationConfig {
	public @Bean ServiceA serviceA() { ... }
}

JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class);
// foo, bar, and serviceA beans will all be available
ctx.getBean(ServiceA.class); // works
ctx.getBean(Foo.class); // works too

这个功能只是为有效模块化 @Configuration 类提供了另一个工具。

使用 @ExternalValue@ResourceBundles 外部化值
一些人建议在 JavaConfig 中引入等同于 PropertyPlaceholderConfigurer 的功能,以便在配置期间访问 properties 文件中的值。M3 正提供了这一点。假设我们有一个典型的 DataSource 需要其 JDBC URL、用户名和密码。像往常一样,这些值存储在 properties 文件中。对于下面的示例,该 properties 文件将在我们的类路径中可用,路径为 com/acme/datasource.properties。该文件的内容如下
datasource.url=jdbc:localhost:...
datasource.username=scott
datasource.password=tiger

使用 @ResourceBundles@ExternalValue,我们现在可以从 JavaConfig 内部访问这些属性


@Configuration
@ResourceBundles("classpath:/com/acme/datasource")
public abstract class ApplicationConfig {
	public @Bean OrderService orderService() {
		return new OrderServiceImpl(orderRepository());
	}

	public @Bean OrderRepository orderRepository() {
		return new JdbcOrderRepository(dataSource());
	}

	public @Bean DataSource dataSource() {
		return new DriverManagerDataSource(url(), username(), password());
	}

	abstract @ExternalValue("datasource.url") String url();
	abstract @ExternalValue("datasource.username") String username();
	abstract @ExternalValue("datasource.password") String password();
}

这里有几点需要注意:请看 @ResourceBundles 注解的值是如何不以 .properties 结尾的?这是因为 JavaConfig 在底层使用了 Spring 的国际化基础设施,并将根据当前区域设置查找 datasource.properties 的变体,例如 datasource_en.properties。此外,虽然此示例为 @ExternalValue 注解提供了字符串值,但默认是根据方法名查找属性。所以,如果我们没有提供 @ExternalValue("datasource.url") String url(),而是只提供了 @ExternalValue String url(),JavaConfig 将会查找名为 'url' 的属性。

下一步是什么?
许多用户一直在询问何时能看到 JavaConfig 的 1.0 版本发布,而且问得很有道理——这确实等待了很长时间!在我们将此软件称为“生产质量”软件之前,仍有许多重要更改需要解决,包括内部实现和公共 API。预计未来几周将出现更频繁的里程碑和候选版本。总之:JavaConfig 现在已经并将在未来继续获得完全支持。如果您想关注进展,请访问 JavaConfig 的 JIRA 问题跟踪,特别是路线图视图。
请求反馈!
如果您已经读到这里,那么很可能至少对 JavaConfig 感兴趣 :) 所以,迈出下一步吧!下载此版本阅读文档,试用一下,然后告诉我们您的想法

[更新 3/27: 文章意外删除,因此重新发布 - 向那些已经发表评论的人致歉,他们的评论在此过程中被删除了。] [更新 4/4: 修复了示例 web.xml 中的一个拼写错误。]

获取 Spring 邮件列表

订阅 Spring 邮件列表,保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速前进。

了解更多

获取支持

Tanzu Spring 通过简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部