TestController.java
@RestController public class TestController { @Autowired private TestClass testClass; @RequestMapping(value = "/test", method = RequestMethod.GET) public void testThread(HttpServletResponse response) throws Exception { testClass.doSomething(); } }
TestClass.java
@Component @Scope("prototype") public class TestClass { public TestClass() { System.out.println("new test class constructed."); } public void doSomething() { } }
As you can see, I'm trying to figure out whether a new TestClass
has been injected when visit "xxx/test". "new test class constructed."
got printed only once(first time I triggered "xxx/test") while I was expecting it printed equally. Is that mean @Autowired
object can only be @Singleton
? How does @Scope
work then?
EDIT:
TestController.java
@RestController public class TestController { @Autowired private TestClass testClass; @RequestMapping(value = "/test", method = RequestMethod.GET) public void testThread(HttpServletResponse response) throws Exception { testClass.setProperty("hello"); System.out.println(testClass.getProperty()); } }
I tried @Valerio Vaudi
solution, registered as Scope(scopeName = "request")
. Here is the three time result when I visit "xxx/test"
(first time)
(second)
(third)
I don't understand why the result is null since it doens't reconstruct a new one each time I use it.
Then I tried @Nikolay Rusev
solution @Scope("prototype")
:
(first)
(second)
(third)
This is rather easy to understand since each time I use it(TestClass), Spring auto-regenerate a new instance of it. But the first scene I still cannot understand since it seems to retain only one new instance for each request.
The real purpose is: In each request lifecycle, a new testClass
is required(if needed), and only one is required. At this moment it seems only ApplicationContext
solution is feasible(which I already knew), but I just want to know if this could be done automatically by using @Component
+ @Scope
+ @Autowired
.
When you work with a prototype bean in a singleton, you have three options to get a new instance of the prototype: Spring can autowire a single prototype instance when it creates the singleton. It's the default framework behavior. Spring can create a new prototype instance on every call to any method of this prototype.
When @Autowired doesn't work. There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.
If the scope is set to prototype, the Spring IoC container creates a new bean instance of the object every time a request for that specific bean is made. As a rule, use the prototype scope for all state-full beans and the singleton scope for stateless beans.
By default, the @Autowired annotation implies that the dependency is required. This means an exception will be thrown when a dependency is not resolved. You can override that default behavior using the (required=false) option with @Autowired .
all the answers above are correct. Controller by default is singleton
and the injected testClass
is instantiated once, because default scoped proxy mode is DEFAULT
from spring doc.
public abstract ScopedProxyMode proxyMode Specifies whether a component should be configured as a scoped proxy and if so, whether the proxy should be interface-based or subclass-based. Defaults to ScopedProxyMode.DEFAULT, which typically indicates that no scoped proxy should be created unless a different default has been configured at the component-scan instruction level.
Analogous to support in Spring XML.
See Also: ScopedProxyMode Default: org.springframework.context.annotation.ScopedProxyMode.DEFAULT
if you want new instance to be injected every time you need, you should change your TestClass
to :
@Component @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) public class TestClass { public TestClass() { System.out.println("new test class constructed."); } public void doSomething() { } }
with this additional configuration the injected testClass
will not be really a TestClass
bean but proxy to TestClass
bean and this proxy will understand the prototype
scope and will return new instance every time is needed.
As mentioned, controller is by default singleton, that's why instantiation and injection of TestClass
is performed only once on its creation.
Solution can be to inject application context and get the bean manually:
@RestController public class TestController { @Autowired ApplicationContext ctx; @RequestMapping(value = "/test", method = RequestMethod.GET) public void testThread(HttpServletResponse response) throws Exception { ((TestClass) ctx.getBean(TestClass.class)).doSomething(); } }
Now, when a TestClass
bean is requested, Spring knowing that it is @Prototype
, will create a new instance and return it.
Another solution is to make the controller @Scope("prototype")
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With