Wednesday, March 24, 2010

What to expect while Migrating to Spring 3

Recently, I migrated one of my applications to Spring 3. The project was previously using Spring 2.0.8. Spring 2 was released in 2006 so it was high time that we migrated to a later version. That and the fact that there are plenty of reasons to migrate to Spring 3. Although Spring 3 was released in December 2009, the milestone and other pre-releases of Spring 3 have been around and being actively used by the Spring community for more than a year.

The application that we wanted to migrate was fairly large with 150k lines of code (tests not included). Since this was a heavily used application, we couldn't afford to have many regression errors. Luckily, we had about 6500 unit tests and about 1700 functional tests running constantly in a continuous integration system. Having so many tests execute every time we changed something basically ensured that we don't have too many regression errors. It also considerably boosts the confidence level when we want to make a change to the application.

The first thing you will notice when upgrading to Spring 3 from Spring 2 is that it is packaged in a bunch of jars (or maven dependencies). This changed in Spring 2.5 and is generally regarded a good move although it makes upgrading a bit of a pain (even with maven). The good part is, the jar/dependency names are pretty self descriptive. It'll take a couple of hit and miss tries to get all the dependencies into the classpath.

Now, lets move on to using Spring 3.
API Compatibility:
Spring 3 is generally API compatible with the older versions. The only major difference you might find is your compiler might warn about generics. Older classes like JdbcTemplate got a generics refit to their APIs.
For example: The return values of methods like queryForList got changed to give a more meaningful signature.
Before:
List<Map> userDetails = jdbcTemplate.queryForList

After:
List<Map<String, Object>> userDetails = jdbcTemplate.queryForList

Apart from these, there are also a few minor API changes where
Example: AbstractBeanFactory.getMergedBeanDefinition(..) returns BeanDefinition instead of RootBeanDefinition.
Most of these API changes are minor and should be easy to resolve.

LifeCycle:
If you are using the any of these beans and you had any of these declared as lazy-init, you may be in for a small surprise.
DefaultMessageListenerContainer
GenericMessageEndpointManager
JmsMessageEndpointManager
SchedulerFactoryBean
SimpleMessageListenerContainer

These components extend a new SmartLifecycle interface and are eligible for autostartup. Previous versions of Spring lacked a nice way to start services in a given order. The depends-on attribute doesn't really help if there are a lot of components to be started, especially if they are spread a bunch of xml files. The Lifecycle interface provided a crude way of achieving ordered bean startup albeit with some manual coding. The Phase, SmartLifecycle interfaces take it a step further and give an ability to automatically start your services when Spring starts up. More on this in a later blog post.

A SmartLifeCycle bean can declare that it needs to be autoStarted (by returning true in the isAutoStartup() method). When the ApplicationContext starts, it looks for any SmartLifeCycle beans that are present in the context, and initializes all of them, even if they are marked as lazy. It then selectively starts the ones that are marked to be autoStarted. This means that if any of these beans are previously declared to be lazy-init, they will be initialized after application context has initialized. This is not really a bug as the isAutoStartup() method is available to Spring only after the bean has been initialized. The consolation if any is that these beans are initialized in a later phase, during the startup of SmartLifecycle beans.

In our application, we were using a few of these beans. While testing, we were counting on these beans not getting initialized due to the lazy-init attribute. But, with Spring 3 these beans were all getting initialized, which was undesirable. The correct resolution was to move these beans out of the bean context into a separate context xml file that is never loaded during testing.

We've been running Spring 3 in production for a little over 2 weeks now, with no issues. Overall, the Spring 3 upgrade was pretty painless and straightforward. I think the Spring team did a good job of trying to preserve API compatibility and to make sure that there were no regression errors in the APIs.

If you are using an older version of Spring, it is time to move to Spring 3.

Ofcourse, you should follow me on twitter.

3 comments:

Android app developer said...

Great idea..Keep it up.Try to get more this kind of post.This is one of the beneficial post.

Anonymous said...

Thanks For the Information. Are you able to Re-factor all your controller without using deprecated controllers of Spring 2.x? If yes can you please post what is the best way to refactor all the types with minimal impact on current implementation.

malpropio said...

Hi, very good post and it's saved me a lot of debugging time but I have a question, I'm using a datasource supplied to me that implements SmartLifeCycle and my problem now is lazy-init="true" and/or autoStartup=false both initialized and start the dataSource which I do now want, I can live with the initialization part but I don't understand why it's still getting started even though autostart is set to false.

Cheers