Spring Java 配置 - M3 版本的新增内容

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

今天标志着 Spring Java 配置项目(简称 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 已弃用
这 hardly算是一个“新功能”,但这个变化很重要,值得一提,因为下面我将讨论的大部分内容都围绕着 JavaConfigApplicationContext,它是 AnnotationApplicationContext 的后继者。为什么会做出这个改变? AnnotationApplicationContext 与Spring 2.5的Annotation-Driven Injection机制存在显著的命名冲突。JavaConfig提供了一种不同于Annotation-Driven Injection的配置方法,所以我们希望通过完全重命名这个类来明确区分这一点。 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 格式。请注意,这些文档也包含在通过SourceForge提供的常规 zip分发版 中。与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 功能的功能,以便在配置期间访问属性文件中的值。M3提供了这一点。假设我们有一个典型的 DataSource,它需要JDBC URL、用户名和密码。和往常一样,这些值存储在属性文件中。对于下面的示例,该属性文件将在我们的类路径中,路径为 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 社区所有即将举行的活动。

查看所有