Quantcast
Channel: Learning the code way
Viewing all articles
Browse latest Browse all 231

Method Injection in Spring

$
0
0
I knew Spring supports two types of injection - setter and constructor. I had also heard of a third type called method injection. But having never come across a use case for the same, I pretty much ignored it. Until I gave an interview.
All was going well until this question came along:
"You have a singleton bean. Is there a way to inject a new bean X into this class whenever you call the getX property of this bean ?"
I was pretty flummoxed. Later on I hunted on the web and came across this post by Rod Johnson.
The below post is an adaption from the same to focus on the interviewer's question.
publicclass SampleTester {

private Glove glove;

public Glove getGlove() {
returnthis.glove;
}

publicvoid setGlove(final Glove glove) {
this.glove = glove;
}

publicboolean testSample(finalString sample) {
// 1: wear a glove
// 2: test the sample
// 3: Dispose glove
// 4: return result
returntrue;
}
}
The SampleTester class is used to test samples. (I know the class seems highly convoluted - bear with me ) The testSample process follows a series of steps. For every sample tested a new pair of gloves is required. Normal code would be:
public boolean testSample(final String sample) {
// 1: wear a glove
Glove gloves = new Glove(); // or some factory method
// 2: test the sample
// 3: Dispose glove
gloves = null;
// 4: return result
returntrue;
}
However due to certain constraints it is not feasible to have the code for glove creation inside. Instead Glove has been defined as a bean which is injected into the class by the container. So the code would be:
public boolean testSample(final String sample) {
// 1: wear a glove
final Glove glove = this.getGlove();
// 2: test the sample
// 3: Dispose glove
//???
// 4: return result
returntrue;
}
This is where we are stuck. How do we dispose the glove safely ? The glove is injected into SampleTester at the time of creation. If we set it to null, than future invocations of the testSample method will fail.
What we need is that every time we make a call to getGloves we should get a new glove.No, the answer is not to treat glove property as a prototype. A new prototype bean is created everytime we need to inject the property as a reference in some bean. Here we need our method to be capable of injecting a new bean everytime it is called. One approach would be :
publicclass SampleTester implements ApplicationContextAware {

private ApplicationContext applicationContextRef;

public Glove getGlove() {
// this is a bean with prototype scope
return (Glove) this.applicationContextRef.getBean("prototypeGlove");
}

publicboolean testSample(finalString sample) {
// 1: wear a glove
Glove glove = this.getGlove();
// 2: test the sample
System.out.println("testing sample " + sample + " with Glove " + glove);
// 4: Dispose glove
glove = null;
// 3: return result
returntrue;
}

@Override
publicvoid setApplicationContext(final ApplicationContext applicationContext)
throws BeansException {
this.applicationContextRef = applicationContext;

}

}
If I were to call the testSample method multiple times, each time the gloves displayed will be different. This code meets the requirement perfectly - with one drawback. Our application code is now tightly coupled with Spring. If we want to avoid that we use method injection.
Method Injection will allow Spring to inject a method into our class. A method that is capable of returning a new glove object everytime we need one.
publicclass SampleTester {

public Glove getFreshGloves() {
System.out.println("This should never be called "
+ "- overriden version will be used");
returnnull;
}

publicboolean testSample(finalString sample) {
// 1: wear a glove
Glove gloves = this.getFreshGloves();
// 2: test the sample
System.out.println("testing sample " + sample + " with gloves " + gloves);
// 4: Dispose glove
gloves = null;
// 3: return result
returntrue;
}

}
If we directly use this bean we will get a Null Pointer Exception. We need Spring to override our getFreshGloves method. That is specified in our xml:
<bean name="sampleTester" class="test.method_injection.SampleTester">
<lookup-method name="getFreshGloves" bean="prototypeGloves"/>
</bean>

<bean name="prototypeGloves" class="test.method_injection.Gloves"
scope="prototype"/>
Here we have specified where the Gloves object for getFreshGloves will come from. Spring Container will actually subclass our SampleTester class and override the method getFreshGloves. Within it, the container will get a bean by the name 'prototypeGloves' and return it. Thus a method has been injected by the container.
As it involves inheritance we obviously cannot define SampleTester as final or set the method testSample to final.
A best practice would be to obtain the method from an interface or inherit an abstract version from a parent class.
With respect to our question it is essential that the bean 'prototypeGloves' is a prototype scoped bean. If it is singleton than every call to getFreshGloves will return the same singleton instance.

Viewing all articles
Browse latest Browse all 231

Trending Articles