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.
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 :
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.
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.
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 {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:
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;
}
}
public boolean testSample(final String sample) {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:
// 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;
}
public boolean testSample(final String sample) {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.
// 1: wear a glove
final Glove glove = this.getGlove();
// 2: test the sample
// 3: Dispose glove
//???
// 4: return result
returntrue;
}
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 {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.
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;
}
}
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 {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:
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;
}
}
<bean name="sampleTester" class="test.method_injection.SampleTester">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.
<lookup-method name="getFreshGloves" bean="prototypeGloves"/>
</bean>
<bean name="prototypeGloves" class="test.method_injection.Gloves"
scope="prototype"/>
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.