Reputation: 65
I have the following Controller and Service
@Controller
public class MyController
{
@Autowired
private MyService myService;
@RequestMapping(method=RequestMethod.GET, value="/connect.do")
public String connect(Model model)
{
invokeService();
return "xxx";
}
private void invokeService()
{
myService.test();
}
}
@Service
public class MyService
{
private int value1 = 200;
private int value2 = 333;
private String value3 ;
private String value4 ;
private String value5 ;
....
public void test()
{
System.out.println(value1++);
foo();
}
private void foo()
{
}
}
When I use 2 browsers to connect to the application, the output is "200" and "201", which means Spring inject the same MyService instance into the controller for different connection.
I need the output to be "200" and "200" when I use 2 different connections to access the application, because I need to share values1, values2, value3, etc between "test()" and "foo()". How to do it in Spring? Basically, I want Spring to inject different instance for different connection. I tried @Scope("prototype") in Service bean and it doesn't work.
I can make it work by using:
@Controller
public class MyController
{
private void invokeService()
{
new MyService.test();
}
}
I am just wondering that how to do it in Spring.
Another way of asking this question is: How to have multiple controller instances (one for each user connection) as opposed to one controller instance serving for all connections?
EDIT: I can see the difference of prototype (output: 200 200 200 200) and singleton (output: 200 201 202 203) by using the follow code and 2 browser connections
private void invokeService()
{
myService = applicationContext.getBean( MyService.class );
new MyService.test();
myService = applicationContext.getBean( MyService.class );
new MyService.test();
}
But when I put "applicationContext.getBean( MyService.class )" in postConstructor instead:
public class MyController implements ApplicationContextAware {
private SearchService searchService;
@PostConstruct
public void init() {
searchService = applicationContext.getBean( SearchService.class );
}
protected ApplicationContext applicationContext;
@Override
public void setApplicationContext( ApplicationContext applicationContext ) throws BeansException {
this.applicationContext = applicationContext;
}
}
and use "@Scope(BeanDefinition.SCOPE_PROTOTYPE)" on MyService, the output are still "200" and "201" when I use 2 browsers to connection to the application
Upvotes: 2
Views: 2692
Reputation: 328860
@Autowired
doesn't mix with @Scope("prototype")
uness you enable proxy generation (which is probably not what you want - it quickly gets complex).
Make your MyController
ApplicationContextAware
instead:
public class MyController implements ApplicationContextAware {
public MyService getMyService() {
return applicationContext.getBean( MyService.class );
}
protected ApplicationContext applicationContext;
@Override
public void setApplicationContext( ApplicationContext applicationContext ) throws BeansException {
this.applicationContext = applicationContext;
}
}
Now @Scope(BeanDefinition.SCOPE_PROTOTYPE)
will work as expected.
Note that you will get a new instance of MyService
every time you call getMyService()
.
Why doesn't your approach (and my old one) doesn't work? MyController
is a singleton, so the field myService
is wired exactly once - Spring can't give you a new instance of the bean every time you access the field for two reasons:
It would break in other ways. For example when you call two methods of the bean:
myService.foo(); myService.bar(); // do you really want a new bean here?
PS: If you use Spring < 3.1.2, you will want to specify the ID of the bean since getBean(Class<?> type)
was very slow for those versions.
Upvotes: 3