I am trying to understand different Bean Scopes. When working with @RequestScope I don't see message printed to console from constructor and destroy() method in order to confirm when this Bean is really being created or destroyed. This works for @Singleton and @Prototype Beans.
Also person1.counter++; doesn't seam to be reset after every request meaning that Bean isn't really recreated for each request as the name might suggest.
But if I add increaseCounter() to Person.java and call that from the Controller then counter is reset and both constructor and destroy() method messages are printed in console. What is going on here. In tutorials about @RequestScope none of this is mentioned.
int increaseCounter() {
counter++;
return counter;
}
I was also able to change the code so that "Person Created" and "Person Destroyed" are being displayed but person1.counter++; is never reset as if the Bean is not really being destroyed.
Person.java
package com.ivoronline.springboot_accessories_beans_scope_request;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class Person {
//PROPERTIES
public String name;
public int counter;
//CONSTRUCTOR
Person() {
System.out.println("Person Created");
}
//DESTROY
@PreDestroy
public void destroy() {
System.out.println("Person Destroyed");
}
}
Controller.java
package com.ivoronline.springboot_accessories_beans_scope_request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
//PROPERTIES
@Autowired Person person1; //New Instance/Bean
@Autowired Person person2; //New Instance/Bean
//=========================================================================================================
// SET PERSON NAME
//=========================================================================================================
@RequestMapping("setPersonName")
void setPerson() {
person1.name = "John";
person2.name = "Bill";
person1.counter++;
}
//=========================================================================================================
// GET PERSON NAME
//=========================================================================================================
@RequestMapping("getPersonName")
String getPerson() {
return person1.counter + ":" + person1.name + " - " + person2.name;
}
}
When using scopes other than singleton
and prototype
it starts to involve lazy proxies.
@RestController
public class Controller {
@Autowired Person person1; //New Instance/Bean
@Autowired Person person2; //New Instance/Bean
@RequestMapping("setPersonName")
void setPerson() {
person1.name = "John";
person2.name = "Bill";
person1.counter++;
}
@RequestMapping("getPersonName")
String getPerson() {
return person1.counter + ":" + person1.name + " - " + person2.name;
}
}
What happens in this class is that 2 proxies for Person
are injected. Those proxies do nothing. When you call a method on the proxy what it actually does is go to the request attributes (as it is a request scoped bean), find an attribute named person1
or person2
and return the Person
instance found (or create a new one if needed).
NOTE: You can also check this and you will probably see that person1
is of type Person$SpringCGLib$....
something and not a Person
directly.
However in your code you are directly accessing public
scoped fields. Which are set on the proxy and not on the actually request scoped object. This is because there is no way to intercept the direct setting/accessing of fields.
Scoped proxies only work with method calls (as you already discovered yourself when you added the increaseCounter
method. So to make this work you will need methods like setName
and increment
to operate on the object and not do field access like you are doing now.
The Spring Framework reference guide also explains this in a section called Scoped Beans as Dependencies.