Spring Tip: Don't Explicitly Call Setter Methods

Posted on July 24, 2007 by Scott Leberknight

Spring has many setter methods in its classes. For example, take a look at the Hibernate support class HibernateTemplate and the Spring MVC base controller BaseCommandController. This tip is very simple:

Setter methods in Spring are for bean configuration only.

For example, say you are implementing a DAO and have extended the Spring support class HibernateDaoSupport which provides a HibernateTemplate for use in data access methods. You might have a method to search for blog entries like this:

public List findBlogEntriesByTitle(String title) {
    return getHibernateTemplate().findByNamedParam(
        "from BlogEntry e where e.title like :theTitle", "theTitle", "%" + title + "%");
}

Now let's say you have a really popular blog and want to limit the number of search results to 100. You check the HibernateTemplate JavaDocs and find there is a setMaxResults() method that looks promising. So you change your method to this:

public List findBlogEntriesByTitle(String title) {
    getHibernateTemplate().setMaxResults(100);  // Don't do this!
    return getHibernateTemplate().findByNamedParam(
        "from BlogEntry e where e.title like :theTitle", "theTitle", "%" + title + "%");
}

The above innocent-looking code has an insidious side-effect, which occurs because HibernateTemplate, like many Spring classes, is intended to be used in a thread-safe manner. In the example, calling setMaxResults sets the maximum number of results for all threads flowing through the HibernateTemplate after this method has been called, instead of just for the current Hibernate Session. While setting the maximum query results may not be a big deal, what if instead you did this:

public void updateAccountProfile(AccountProfile profile) {
    HibernateTemplate ht = getHibernateTemplate();
    int originalFlushMode = ht.getFlushMode();
    ht.setFlushMode(FlushMode.FLUSH_EAGER);  // Don't do this!
    ht.update(profile);
    ht.setFlushMode(originalFlushMode);
}

This is much worse as now the Hibernate flush mode is changed for every thread using the HibernateTemplate and causes non-deterministic flushing behavior. I have seen a project encounter this very bug and it was initially difficult to track down since most of the time everything worked just fine. As an aside, you should rarely if ever need to explicitly flush a Hibernate Session in application code.

In general, setter methods in Spring classes are intended for initial bean configuration, and should not be called at runtime as they are generally not thread-safe. So, if you want to call a setter method, your best bet is to look for an alternate, thread-safe solution. In the above example, the solution could be to use a HibernateCallback which passes you a Hibernate Session on which you can set the flush mode only for that particular Session. In any case, making sure your team understands this could save some headaches down the road, especially for developers who are new to Spring and who are used to using setter methods to change object properties at runtime, not just as an initial configuration mechanism.

Enforce Required Dependencies in Spring 2.0

Posted on July 10, 2007 by Scott Leberknight

Spring 2.0 added the @Required annotation that allows you to define which bean properties are required to be injected. In combination with the RequiredAnnotationBeanPostProcessor, Spring will blow up at application startup if any dependencies are not satisfied. In my (admittedly limited) testing of this feature, only one unsatisfied dependency is reported at a time - in other words Spring fails fast at the first missing dependency. In any case, the following is all you need to do to enable this feature.

First, annotate the setter methods for required properties.

// In some class that requires a UserDao to be injected.
@Required
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

Second, in one of your Spring application context files, add the following:

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

That's it. If you want you can configure the RequiredAnnotationBeanPostProcessor to look for a different type of annotation, for example maybe you want your own @Injected annotation:

// In some class that requires a UserDao to be injected.
@Injected
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
    <property name="requiredAnnotationType" value="com.acme.annotation.Injected"/>
<bean>

Overall this is a nice feature that might save you some time debugging a missing dependency and at the same time making your code a little more explicit, since the annotated setter methods tell the reader something, i.e. that this is a "special" property that is required to be set before the class can successfully be used.