领先一步
VMware 提供培训和认证,助您加速进步。
了解更多今天标志着 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的好处是很直接的:
考虑到这一点,让我们来看看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中的一个拼写错误。]