领先一步
VMware 提供培训和认证,助您加速前进。
了解更多今天标志着 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 的好处显而易见
考虑到这些,让我们来看看 M3 版本中有哪些变化
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));
}
}
<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”方法。此更改完善了这种可能性。
@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 类提供了另一个工具。
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' 的属性。
[更新 3/27: 文章意外删除,因此重新发布 - 向那些已经发表评论的人致歉,他们的评论在此过程中被删除了。] [更新 4/4: 修复了示例 web.xml 中的一个拼写错误。]