Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prototype Bean doesn't get autowired as expected

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)

  • new test class constructed.
  • null

(second)

  • null

(third)

  • null

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)

  • new one constructed.
  • new one constructed.
  • null

(second)

  • new one constructed.
  • new one constructed.
  • null

(third)

  • new one constructed.
  • new one constructed.
  • null

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.

like image 591
Kim Avatar asked Mar 29 '16 07:03

Kim


People also ask

Can we Autowire prototype bean?

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.

Why is Autowired not working?

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.

How do you set a bean scope as a prototype?

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.

What will happen if I make @autowired as false?

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 .


2 Answers

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.

like image 122
Nikolay Rusev Avatar answered Sep 21 '22 09:09

Nikolay Rusev


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").

like image 28
Alex Salauyou Avatar answered Sep 21 '22 09:09

Alex Salauyou