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 配置中可用的功能集是一致的。有关如何使用 JavaConfig 的完整详细信息,请查看参考文档。如果您不熟悉 JavaConfig,请务必查看快速入门部分。

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

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

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

AnnotationApplicationContext 已弃用
这几乎不是一个“新特性”,但必须提及此更改,因为我下面将讨论的大部分内容都围绕着JavaConfigApplicationContext展开,它是AnnotationApplicationContext的后续版本。 *为什么进行此更改?* AnnotationApplicationContext 与 Spring 2.5 的 *基于注解的注入* 功能存在重大的命名冲突。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格式提供。请注意,此文档也作为通过 SourceForge 提供的常规zip 分发的一部分打包。与 M3 中的所有新增内容一样,您对文档的反馈将有助于在项目走向 1.0 GA 版本的过程中改进它。
在 Web 层中支持 JavaConfig
在此版本之前,必须通过 XML “引导” JavaConfig 才能与 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 新闻通讯保持联系

订阅

获得支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多信息

即将举行的活动

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

查看全部