Spring Basics: XML Setter Injection With Custom Method Names

In a previous blog post we ended up with the following XML-based configuration:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="firstStreak" class="com.relentlesscoding.wirebeans.PositiveStreak">
        <constructor-arg ref="startTime"/>
    </bean>
    <bean id="secondStreak" class="com.relentlesscoding.wirebeans.PositiveStreak">
        <constructor-arg ref="startTime"/>
    </bean>

    <util:list id="myRunningStreaks">
        <ref bean="firstStreak"/>
        <ref bean="secondStreak"/>
    </util:list>

    <bean id="running" class="com.relentlesscoding.wirebeans.Running">
        <property name="streaks" ref="myRunningStreaks"/>
    </bean>

</beans>

The complete code can be found on GitLab.

To recap, we instantiate a bean of type LocalDate by invoking its now() static method. We inject this bean into two PositiveStreak beans using constructor injection. We reference the LocalDate bean by its id, startTime. Next, we create a bean that is a list of PositiveStreaks using the util namespace. We then inject this list into a Habit class called Running using setter injection.

For setter injection to work from XML, the setter method needs to follow Java bean conventions. This means that there should be setter method called set<PropertyName>, where the important part is that the method start with set. The name of the property itself can be something completely different from the field it is setting, Spring does not care. For example, the following would work without a hitch:

class Running implements Habit {
    private List<Streak> myStreakList;

    public void setStreaks(List<Streak> aListOfStreaks) {
        this.myStreakList = aListOfStreaks;
    }
}
<bean class="com.relentlesscoding.wirebeans.Running">
    <property name="streaks" ref="myRunningStreaks" />
</bean>

The only important thing is that the name attribute of the property element corresponds to the Property part of setProperty setter method in Java. To reiterate: The name of the field that is being set by the mutator method can be something completely different from the name attribute of the property element.

But sometimes we want to be creative with our method names if that makes the code more readable, or maybe we are dealing with code that we cannot change. If we end up with a mutator method that does not start with set, you are now in a position to appreciate that Spring will not find this method and throws a BeanCreationException.

Handling Mutators With a Non-Standard Names

On StackOverflow a solution was proposed that uses a MethodInvokingFactoryBean. But since we are invoking a method that does not return a result, the documentation recommends we use MethodInvokingBean instead.

Suppose we have the follow setter in place:

class Running implements Habit {
    private List<Streak> myStreakList;

    public void replaceStreaks(List<Streak> aListOfStreaks) {
        this.myStreakList = aListOfStreaks;
    }
}

We could leverage a MethodInvokingBean like so:

<bean id="running" class="com.relentlesscoding.wirebeans.Running">

<bean id="caller" class="org.springframework.beans.factory.config.MethodInvokingBean">
    <property name="targetObject" ref="running"/>
    <property name="targetMethod" value="replaceStreaks"/>
    <property name="arguments" ref="myRunningStreaks"/>
</bean>

(Notice that we actually use setter injection on the MethodInvokingBean by specifying properties on it.) The targetObject attribute specifies on which instance a targetMethod should be invoked. We can specify our custom mutator name, and pass a reference to our arguments.

This is verbose, so think carefully before you violate the Java bean naming convention. Consider using a facade if the code is not under your control. Alternatively, Java-based Spring configuration with the @Autowired annotation will work on a method with any name.

Leave a Reply

Your email address will not be published. Required fields are marked *