Spring Basics XML Setter Injection With Custom Method Names
Let’s have a look at how to inject beans using setters with a custom method name.
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
PositiveStreak
s 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.