@Scope("prototype") bean scope not creating new bean

SpringSpring Mvc

Spring Problem Overview


I want to use a annotated prototype bean in my controller. But spring is creating a singleton bean instead. Here is the code for that:

@Component
@Scope("prototype")
public class LoginAction {

  private int counter;

  public LoginAction(){
	System.out.println(" counter is:" + counter);
  }
  public String getStr() {
	return " counter is:"+(++counter);
  }
}

Controller code:

@Controller
public class HomeController {
	@Autowired
    private LoginAction loginAction;

	@RequestMapping(value="/view", method=RequestMethod.GET)
    public ModelAndView display(HttpServletRequest req){
	    ModelAndView mav = new ModelAndView("home");
		mav.addObject("loginAction", loginAction);
    	return mav;
	}

    public void setLoginAction(LoginAction loginAction) {
	    this.loginAction = loginAction;
	}

    public LoginAction getLoginAction() {
	    return loginAction;
    }
    }

Velocity template:

 LoginAction counter: ${loginAction.str}

Spring config.xml has component scanning enabled:

	<context:annotation-config />
    <context:component-scan base-package="com.springheat" />
    <mvc:annotation-driven />

I'm getting an incremented count each time. Can't figure out where am I going wrong!

Update

As suggested by @gkamal, I made HomeController webApplicationContext-aware and it solved the problem.

updated code:

@Controller
public class HomeController {

    @Autowired
    private WebApplicationContext context;

    @RequestMapping(value="/view", method=RequestMethod.GET)
    public ModelAndView display(HttpServletRequest req){
	    ModelAndView mav = new ModelAndView("home");
	    mav.addObject("loginAction", getLoginAction());
		return mav;
    }

	public LoginAction getLoginAction() {
    	return (LoginAction) context.getBean("loginAction");
    }
}

Spring Solutions


Solution 1 - Spring

Scope prototype means that every time you ask spring (getBean or dependency injection) for an instance it will create a new instance and give a reference to that.

In your example a new instance of LoginAction is created and injected into your HomeController . If you have another controller into which you inject LoginAction you will get a different instance.

If you want a different instance for each call - then you need to call getBean each time - injecting into a singleton bean will not achieve that.

Solution 2 - Spring

Since Spring 2.5 there's a very easy (and elegant) way to achieve that.

You can just change the params proxyMode and value of the @Scope annotation.

With this trick you can avoid to write extra code or to inject the ApplicationContext every time that you need a prototype inside a singleton bean.

Example:

@Service 
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)  
public class LoginAction {}

With the config above LoginAction (inside HomeController) is always a prototype even though the controller is a singleton.

Solution 3 - Spring

Just because the bean injected into the controller is prototype-scoped doesn't mean the controller is!

Solution 4 - Spring

@controller is a singleton object, and if inject a prototype bean to a singleton class will make the prototype bean also as singleton unless u specify using lookup-method property which actually create a new instance of prototype bean for every call you make.

Solution 5 - Spring

As mentioned by nicholas.hauschild injecting Spring context is not a good idea. In your case, @Scope("request") is enough to fix it. But let say you need several instances of LoginAction in controller method. In this case, I would recommend to create the bean of Supplier (Spring 4 solution):

    @Bean
    public Supplier<LoginAction> loginActionSupplier(LoginAction loginAction){
        return () -> loginAction;
    }

Then inject it into controller:

@Controller
public class HomeController {
    @Autowired
    private  Supplier<LoginAction> loginActionSupplier;  

Solution 6 - Spring

Using ApplicationContextAware is tying you to Spring (which may or may not be an issue). I would recommend passing in a LoginActionFactory, which you can ask for a new instance of a LoginAction each time you need one.

Solution 7 - Spring

use request scope @Scope("request") to get bean for each request, or @Scope("session") to get bean for each session 'user'

Solution 8 - Spring

A protoype bean injected inside a singelton bean will behave like singelton untill expilictly called for creating a new instance by get bean.

context.getBean("Your Bean")

Solution 9 - Spring

> By default, Spring beans are singletons. The problem arises when we > try to wire beans of different scopes. For example, a prototype bean > into a singleton. This is known as the scoped bean injection problem.

Another way to solve the problem is method injection with the @Lookup annotation.

Here is a nice article on this issue of injecting prototype beans into a singleton instance with multiple solutions.

https://www.baeldung.com/spring-inject-prototype-bean-into-singleton

Solution 10 - Spring

@Component

@Scope(value="prototype")

public class TennisCoach implements Coach {

// some code

}

Solution 11 - Spring

Your controller also need the @Scope("prototype") defined

like this:

@Controller
@Scope("prototype")
public class HomeController { 
 .....
 .....
 .....

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontintinView Question on Stackoverflow
Solution 1 - SpringgkamalView Answer on Stackoverflow
Solution 2 - Springdb80View Answer on Stackoverflow
Solution 3 - SpringDave NewtonView Answer on Stackoverflow
Solution 4 - SpringkartheekView Answer on Stackoverflow
Solution 5 - SpringIhor RybakView Answer on Stackoverflow
Solution 6 - Springnicholas.hauschildView Answer on Stackoverflow
Solution 7 - SpringBassem Reda ZohdyView Answer on Stackoverflow
Solution 8 - SpringUjjwal ChoudhariView Answer on Stackoverflow
Solution 9 - SpringSaikatView Answer on Stackoverflow
Solution 10 - SpringRakesh Singh BalharaView Answer on Stackoverflow
Solution 11 - SpringflyerfangView Answer on Stackoverflow