领先一步
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 配置中可用的功能集是一致的。有关如何使用 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 中的错别字。]