Reputation: 5867
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");
}
}
Upvotes: 162
Views: 260008
Reputation: 151
class with @Configuration
@Bean
@RequestScope //composed annotation for @Scope(value=...proxyMode=...)
public LoginAction requestScopedBean() {
return new LoginAction ();
}
@Controller
public class HomeController {
@Resource(name = "requestScopedBean")
private LoginAction loginAction;
//...your code here
Upvotes: 0
Reputation: 59
Your controller also need the @Scope("prototype")
defined
like this:
@Controller
@Scope("prototype")
public class HomeController {
.....
.....
.....
}
Upvotes: -12
Reputation: 16750
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
Upvotes: 1
Reputation: 410
@Component
@Scope(value="prototype")
public class TennisCoach implements Coach {
// some code
}
Upvotes: -1
Reputation: 421
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")
Upvotes: 2
Reputation: 4427
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.
Upvotes: 43
Reputation: 3279
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;
Upvotes: 6
Reputation: 12942
use request scope @Scope("request")
to get bean for each request, or @Scope("session")
to get bean for each session 'user'
Upvotes: 3
Reputation: 42849
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.
Upvotes: 3
Reputation: 320
@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.
Upvotes: 12
Reputation: 160181
Just because the bean injected into the controller is prototype-scoped doesn't mean the controller is!
Upvotes: 19
Reputation: 21000
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.
Upvotes: 193