Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreToday marks the third milestone release of the Spring Java Configuration project (JavaConfig for short). The release contains numerous bug fixes and new features - I'll highlight a few of the most interesting changes below, but first let me give a quick refresher as to what JavaConfig is all about.
If you have any experience with Spring, the following snippet of XML configuration will likely be familiar. Let's assume we're looking at a file named 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>
Of course, this XML configuration will ultimately serve as a set of instructions for a Spring ApplicationContext to instantiate and configure our beans:
ApplicationContext ctx = new ClassPathXmlApplicationContext("application-config.xml");
OrderService orderService = (OrderService) ctx.getBean("orderService");
JavaConfig simply provides another mechanism to configure the Spring IoC container, this time in pure Java rather than requiring XML to get the job done. Let's port the configuration above to 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 ...
}
}
Like the original XML file, this class is simply a set of instructions as to how to construct our application's various components. We'll supply these instructions to an ApplicationContext implementation specifically designed to read and execute Java-based configuration instructions:
JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class);
OrderService orderService = ctx.getBean(OrderService.class);
And that's it! Well, almost. Of course there's a lot more to JavaConfig, but for the most part the feature set is 1:1 with what's available in Spring's XML config. For full details on how to use JavaConfig, take a look at the reference documentation. If you're new to JavaConfig, be sure to check out the quick start section.
At any rate, the benefits of JavaConfig are straightforward:
With that in mind, let's take a look at what's changed in the M3 release:
JavaConfigApplicationContext context = new JavaConfigApplicationContext(AppConfig.class);
OrderService orderService = context.getBean(OrderService.class);
Look ma, no casting! And no string-based lookups, either. Of course, this will beg the question, "what if two or more objects of type OrderService have been configured in the context?" This is a situtation that could easily occur, and there are multiple ways to address it. For brevity in this post, I'll simply refer those interested to take a look at the disambiguation options section of the reference documentation.
These type-safe getBean() methods have also been fitted onto the ConfigurationSupport base class, such that the following is possible:
@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>
In practice, it is likely that many folks will want to continue using a combination of XML and JavaConfig, especially in web applications. This approach still works just fine (see combining configuration approaches), but it was important to the team that we offer users a truly "XML-free" approach if they so desire it. This change rounds out that possibility.
@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
This functionality simply provides another tool for effectively modularizing @Configuration classes.
datasource.url=jdbc:localhost:...
datasource.username=scott
datasource.password=tiger
Using @ResourceBundles and @ExternalValue, we can now access these properties from within 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();
}
A couple of things to note here: see how the value of the @ResourceBundles annotation doesn't end in .properties? This is because JavaConfig is using Spring's internationalization infrastructure underneath, and will look for variations on datasource.properties such as datasource_en.properties according to the current locale. Also, while this example supplied string values to the @ExternalValue annotation, the default is to look up properties based on the method name. So, if we hadn't supplied @ExternalValue("datasource.url") String url(), and rather just @ExternalValue String url(), JavaConfig would have looked for a property named 'url'.
[Update 3/27: Post was accidentally deleted, and so re-posted - apologies to those who had already written comments, as they were deleted in the process.] [Update 4/4: fixed a typo in the sample web.xml.]