I am learning Spring and using XML-way to create 2 beans instances of a POJO Class named "Notepad". finally I got two instances:
n1 = Notepad@56dc1551,hashCode=267513570
n2 = Notepad@102cec62,hashCode=267513570
different toString @value, same hashCode!
Notepad.java
public class Notepad {
private int a;
public Notepad() {
System.out.println(" constructor called. this="+this+" hashCode="+this.hashCode());
}
public int getA() {
System.out.println(" this="+ this +" getA(),a="+a);
return a;
}
public void setA(int a) {
System.out.println(" this="+ this +" setA("+a+")");
this.a = a;
}
}
scope-beans.xml
...
<bean id="notepad" class="Notepad" scope="prototype">
<aop:scoped-proxy />
</bean>
...
Test.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:scoped-beans.xml")
public class Test {
@Autowired
private ApplicationContext context;
@Test
public void testProxyScope() {
System.out.println("containsBean(notepad)?"+context.containsBeanDefinition("notepad"));
var n1 = context.getBean("notepad",Notepad.class);
var n2 = context.getBean("notepad",Notepad.class);
n1.setA(1);
n2.setA(2);
System.out.println("n1="+n1+" n1.a="+n1.getA());
System.out.println("n2="+n2+" n2.a="+n2.getA());
System.out.println("n1="+n1+",hashCode="+n1.hashCode());
System.out.println("n2="+n2+",hashCode="+n2.hashCode());
System.out.println("notepad1==notepad2? "+(n1==n2)); //???
assertNotSame(n1, n2); //???
}
}
containsBean(notepad)?true
constructor called. this=chp3.myapp.Notepad@4c2bb6e0 hashCode=1277933280
this=chp3.myapp.Notepad@4c2bb6e0 setA(1)
constructor called. this=chp3.myapp.Notepad@2dc9b0f5 hashCode=768192757
this=chp3.myapp.Notepad@2dc9b0f5 setA(2)
constructor called. this=chp3.myapp.Notepad@6531a794 hashCode=1697752980
this=chp3.myapp.Notepad@6531a794 getA(),a=0
constructor called. this=chp3.myapp.Notepad@3b5fad2d hashCode=996125997
n1=chp3.myapp.Notepad@3b5fad2d n1.a=0
constructor called. this=chp3.myapp.Notepad@5e17553a hashCode=1578587450
this=chp3.myapp.Notepad@5e17553a getA(),a=0
constructor called. this=chp3.myapp.Notepad@3eb91815 hashCode=1052317717
n2=chp3.myapp.Notepad@3eb91815 n2.a=0
constructor called. this=chp3.myapp.Notepad@56dc1551 hashCode=1457263953
n1=chp3.myapp.Notepad@56dc1551,hashCode=267513570
constructor called. this=chp3.myapp.Notepad@102cec62 hashCode=271379554
n2=chp3.myapp.Notepad@102cec62,hashCode=267513570
notepad1==notepad2? true
java.lang.AssertionError: expected not same
I am expecting only create 2 instance, n1 != n2. but seems n1 n2 created multiple time, even when I do get and set! And weirdly, n1 == n2 finally! the code is very simple, I have no idea where the problem is...
The <aop:scoped-proxy />
is the problem. It does exactly what you told it to do. Create a proxy for the given scope. Which means each time it is being used it will create an instance (due to the scope="prototype"
.
Leave the scope="prototype"
and remove the <aop:scoped-proxy />
as you don't want a scoped proxy, you want that each time you call getBean
get a fresh instance for use.
Scoped proxies are useful for web based scoped (like request
, session
). That way you can inject a reference and when needed it will actually delegate to the actual object in either the request
or session
. For prototype
that means for each call to a method it would need to construct a new instance (which is what you see).
The call to getBean
will actually return the proxy and not an actual instance of the object. That will only be created when needed, which is when calling the methods.