Spring Java Configuration - What's New in M3

Chris Beams

Today 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:

  • It's pure Java, so there's no XML required
  • You get all the benefits of object-orientation in your configuration code
  • It's type-safe and refactoring-friendly
  • You still get the full power of the core Spring IoC container

With that in mind, let's take a look at what's changed in the M3 release:

AnnotationApplicationContext deprecated

Hardly a 'new feature', but this change is important to mention because much of what I'll discuss below revolves around JavaConfigApplicationContext, the successor to AnnotationApplicationContext. Why was this change made? AnnotationApplicationContext posed a significant naming collision with Spring 2.5's Annotation-Driven Injection facility. JavaConfig presents a different approach to configuration than Annotation-Driven Injection, so we wanted to make this distinction clear by renaming the class entirely. AnnotationApplicationContext will remain deprecated until the 1.0.0.rc1 release, at which point it will be removed permanently.

Type-safety improvements

While the above-mentioned JavaConfigApplicationContext behaves largely like it's predecessor, it also introduces type-safe getBean() methods that take full advantage of generics. The following code now works (and from this point forward is the preferred approach to use with JavaConfig):

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));
        }
}
Major documentation update

We've worked hard to bring JavaConfig's documentation up to par with the quality for which Spring is famous. As linked above, the reference documentation is available in HTML as well as PDF formats. Note that this documentation is also packaged as part of the regular zip distributions available via SourceForge. As with all the additions in M3, your feedback on the documentation will help improve it as the project moves forward to its 1.0 GA release.

Support for JavaConfig in the web tier

Prior to this release, JavaConfig had to be 'bootstrapped' via XML in order to be used in conjunction with Spring's ContextLoaderListener and DispatcherServlet classes. To address this limitation, JavaConfigWebApplicationContext has been added. Simply specify this class as the contextClass parameter in your web.xml, and you can use your @Configuration classes directly:

<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.

Modularity improvements with new @Import annotation

Much like Spring XML config's <import/> element, it is now possible to have one @Configuration class import another (and thus all its bean definitions):

@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.

Externalizing values with @ExternalValue and @ResourceBundles

Several have suggested introducing functionality in JavaConfig equivalent to that of PropertyPlaceholderConfigurer for the purpose of accessing values in properties files during configuration. M3 provides just that. Let's assume we have a typical DataSource that needs its JDBC url, username and password. As per usual, these values are stored in a properties file. For the example below, that properties file will be available in our classpath at com/acme/datasource.properties. Contents of that file are as follows:

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'.

What's next?

A number of users have been asking when we'll see a 1.0 release of JavaConfig, and with good reason - it's been a long time coming! There remain a number of important changes, both regarding internals as well as the public API that must be addressed before we're ready to call this 'production quality' software. Expect to see more frequent milestones and release candidates in the coming weeks. Bottom line: JavaConfig is now and will continue to be fully supported going forward. If you'd like to keep an eye on progress, please visit JavaConfig's JIRA issue tracking, especially the road map view.

Feedback Requested!

If you've made it this far through the post it's a safe bet you're at least interested in JavaConfig :) So take the next step! Download the release, read the documentation, give it a spin and let us know what you think.

[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.]

 

18 responses


  1. Both in your post's and the documentation's example web.xml appears org.springframework.config.java.JavaConfigWebApplicationContext, thought it is org.springframework.config.java.CONTEXT.JavaConfigWebApplicationContext.

    Why do you have @Configuration annotation? JavaConfig seems to be working fine without defining on a configuration class.


  2. Hi Peter. Regarding the typo in the package name, this post actually does have the correction (thank you). I\'ve also fixed it in svn for the documentation.

    As for @Configuration, you\'re quite right - it is not strictly required. I\'ve put it in the examples simply to be explicit, but the annotation actually serves three potential purposes:

    1. Much like the element in XML, @Configuration exposes attributes that allow the user to set defaults across all enclosed bean definitions
    2. @Configuration itself is annotated as a Spring 2.5 @Component, which means that your Java configurations can be picked up during component-scanning.
    3. Classes annotated with @Configuration are more efficient to process, as JavaConfig does not need to scan for @Bean methods (that being said, the cost is surely negligible in most cases)

    I\'ll update the documentation to reflect this information - thanks!


  3. JavaConfig looks to be heading in the right direction and thanks for the work you guys are putting in it.


  4. @ExternalValue is good step to make JavaConfig production-ready, from the perspective of maintaining configuration values. It's not clear how one can use it to support the typical case where different values are needed in different environment, e.g., developer vs integration vs production.


  5. Can we use Spring JavaConfig in OSGI environmemnt ?


  6. Hi Igor. First of all, JavaConfig is not yet shipping as an OSGi bundle (see SJC-64). Beyond that, using it in an OSGi context is a bit of an unknown insofar as that it hasn't been tried. Initially, JavaConfig will need to be bootstrapped via XML (with ConfigurationPostProcessor), in order to avoid proliferating JavaConfig-specific OSGI ApplicationContexts. But I'm getting ahead of myself - the bottom line is that this is certainly important, and SJC-89 has been created to ensure compatibility before we release 1.0. I'd suggest putting a watch on that issue.

    http://jira.springframework.org/browse/SJC-89


  7. What is the future of Java Config, when Spring 2.5 supports Annotation based configuration ?
    Not that I want to undermine the efforts of Java Config team, but what value does it provide over spring core's annotation based config ?


  8. Would it be possible for Guice to become the IoC/DI underpinning Spring. A lot of the things (and more) you're trying to achieve with JavaConfig is already implemented in that framework.


  9. Would it be possible to use extension method provided by the language instead of the use of @Import annotation?

    @Import(FooConfig.class)
    @Configuration
    public class ApplicationConfig {

    would become

    @Configuration
    public class ApplicationConfig extends FooConfig {

    You can write your ApplicationConfig skeleton and then extend it as you wish.


  10. Very nice. JavaConfig enriches the dev's toolset. I'm reminded of the old Java classes that were used to configure EJBs before XML was used. Seems so long ago! It was not nice to have to maintain and compile stuff just to configure other stuff. Perhaps this is a drawback; or I'm missing something in what JavaConfig is?

    I think A Groovy Spring builder (GroovyConfig?) would be cool. I'm sure there is one around somewhere. This would be a great alternative to XML config, and could grow to be a DSL. Of course, a true DSL, as Drools created for their framework would be optimal.

    — Josef


  11. @bhaskar:
    Spring 2.5's annotation-driven injection presents a different model for configuration. In JavaConfig, configuration information remains centralized within @Configuration classes, while in Spring 2.5's new support, configuration annotations are distributed throughout your components and then autowired. JavaConfig follows very closely the model of Spring XML configuration, but provides a pure-Java way of expressing the configuration metadata.

    @Jan:
    While there are some similarities (100% java, type-safe config, etc), Guice and JavaConfig differ in a number of fundamental ways. Briefly put, JavaConfig works very closely with the core Spring IoC container, and Guice does not; JavaConfig favors a fully centralized approach to expressing DI logic (within @Configuration classes), while Guice favors DI annotations in application classes. If there are particular features you'd like to see in JavaConfig, feel free to enter them in JIRA (http://jira.springframework.org/browse/SJC).

    @Pierangelo:
    Yes, polymorphic @Configurations are fully supported. @Import simply provides another mechanism for modularizing configurations. You may use either or both of these approaches, depending on the situation. Right tool for the job :)

    @j. betancourt:
    Regarding recompilation issues, this is really a matter of style, preference, and requirements. If you have requirements around not needing to recompile, use XML, by all means. If you like the XML style, use it for sure. Spring IDE provides extraordinarily good support so there's no reason not to. However, if you want the full power of Java and object-orientation in your configurations, want better type-safety, or simply prefer not to work with XML so much, JavaConfig is a great option. We're not presenting any one 'silver bullet', but rather tools for devs to choose from. As I mentioned above to Pierangelo, it's about using the right tool for the job.


  12. Great to the see the Type-safety improvements! I found it rather annoying to configure components in type-safe Java but the having to fall back to use names and casts.

    This won't core Spring components which are designed to be configured via XML. An example of that is the LocalSessionFactoryBean, which has a setHibernateProperties method, which is rather cumbersome to use and not type-safe at all. I hope to see improvements in that direction, too.

    Another request I have: Please add sources to the maven distribution!
    It would also help to put the necessary dependency-block for the pom.xml file on the project page.

    Regards
    Jörn


  13. @Pierangelo
    A problem with using the 'extends' keyword for importing another configuration class is that Java only supports single inheritance, so, you can't extend multiple modules.

    Chris, with the @Import solution, how can I get a bean from the imported context in the current context? I mean:

    @Import(DataBaseConfig.class)
    @Configuration
    public class ApplicationConfig {
    @Bean
    public OrderService orderService() {
    return new OrderService(dataSource());
    // this dataSource comes from the imported context
    }
    }

    Would I have to declare an abstract 'dataSource()' method?

    And if you could declare imports like this:

    @Configuration
    public abstract class ApplicationConfig {
    @Import
    protected abstract DataBaseConfig dataBaseConfig();

    @Bean
    public OrderService orderService() {
    return new OrderService(dataBaseConfig().dataSource());
    }
    }

    This would be much more type-safe, and convenient, since you wouldn't have to re-declare the abstract method for each dependency.


  14. @Tetsuo

    This is certainly an interesting idea! Please log a new feature request in JIRA about this so it can be considered fully.


  15. Is it possible with JavaConfig to inject constructor dependencies into a bean that haven't been declared as beens as you can do in Guice?

    i.e. can we construct an object like this:
    public Doc(Leg leg)

    without having a bean of type Len registered? I don't think you can do this with Spring XML as with auto wiring, all dependencies must be declared . correct?


  16. Hi Anthony,

    Yes, it's quite easy to do what you're asking about:

    @Configuration
    public class AppConfig {
    @Bean
    public Doc doc() {
    return new Doc(new Leg());
    }
    }

    That's it! You have full programmatic control over object creation, so do as you please.


  17. I am looking to solve a config problem , and I am not sure if JavaConfig will help me out in that. Please let me know if it can and how. If it is not possible via JavaConfig, then can you point me elsewhere that would help.

    The problem is thus:
    Typical Spring configurations (XML or otherwise) assume that we know at deploy/config time , what the dependency of one bean would be, say "OrderService" depends on "OrderRepository".

    But we have a case where OrderService depends on an "interface" "OrderRepository" and there are atleast 5 to 10 different implementations of that interface. Secondly we do not know until runtime(based on certain values retrieved from the DB and user request) which particular implementation of OrderRepository interface would be wired in.

    We have a home-grown Database-backed way of resolving it. Spring claims that it also has a Database-backed configuration mechanism, but I haven't been able to locate a solution.

    Your answer is greatly appreciated.
    Thanks.


  18. Hi Kasturi,

    Assuming there are a finite number of OrderRepository implementations, and they're known up front, you might consider developing something like a 'DelegatingOrderRepository' that takes as dependencies bean instances of all of the known repositories. Inject the DelegatingOrderRepository instance into your OrderService, and give DelegatingOrderRepository the ability to introspect into the current request and decide, on a call-by-call basis, which of the delegate repositories to invoke. Assuming you're using Spring MVC, consider using the RequestContextHolder ThreadLocal to get hold of the current request from within your DelegatingOrderRepository implementation.

    As far as determining which implementations are available in the database, consider implementing a FactoryBean to help you do this. For example, DelegatingOrderRepositoryFactoryBean could, in it's getObject() method, be responsible for finding all known implementations in the database, instatiating them as appropriate, injecting those instances into a DelegatingOrderRepository instance, and then return that instance. Of course, if you're not already familiar, you'll want to read up on the documentation for FactoryBean to understand how it plugs into the container lifecycle.

    With regard to JavaConfig, you certainly could use JavaConfig to do all this, but it won't provide you any fundamental advantage over doing it in XML. You'd get the type safety and ease-of-use of pure Java, but everything I just described can be done easily from XML, too. If you already know Spring XML, you should probably stick with that in this case.

3 trackbacks

Leave a Reply

Quote selected text