最近在项目中有一个service中使用了类成员变量,导致了业务并发。(多个人同时使用一个功能) 于是就在此service上添加注解(@Scope("prototype"))使用多例,但是问题出现了,添加了此注解还是存在此问题,于是看了spring Autoware 的加载机制,网上有很多资料,看后也做了测试,得出一下结论。
使用 @Autoware 注入service (多例) 其实每个方法使用的都是同一个service对象,并未出现多例模式(修正:service中@Scope("prototype")的多例模式体现在不同Controller中,单个Controller中使用@Autoware注入的service一直都是同一个,不会重复创建。),测试代码
private ScopeTestService scopeTestService;
@GetMapping("/test1")
@ResponseBody
public ServerResponse test1(){
scopeTestService.test1();
log.info(scopeTestService.toString());
return ServerResponse.createBySuccess("test1");
}
@GetMapping("/test2")
@ResponseBody
public ServerResponse test2(){
scopeTestService.test2();
log.info(scopeTestService.toString());
return ServerResponse.createBySuccess("test2");
}
结果:此方式访问两个接口打印的scopeTestService 是一样的 com.barton.service.impl.ScopeTestServiceImpl@49c4d534
使用getBean 获取 scopeTestService 成功
以上是@Autoware 和 getBean()在多例模式上的小小区别,至于其他方面的区别没再去细细研究
其实多例模式下直接将第一中方式的Controller 变为多例模式(prototype)也可以解决并发问题,简单粗暴。
那么接下来就又引出来一个疑问,Spring的不止prototype作用域是多例的,request也是多实例的,这两种方式又存在怎样的区别呢?
spring bean作用域有以下5个:
singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;
(下面是在web项目下才用到的)
-
request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听; -
session:每次会话,同上; -
global session:全局的web域,类似于servlet中的application。
我们知道,spring有singleton和prototype作用域,而且从spring2.0开始,又增加了三种作用域,request、session、global session。session、global session的作用域是HTTP session和全局session。对于request和prototype,每一次请求都会产生一个新的bean实例,那么它们的具体有什么区别呢?而且,Spring不能对一个prototype bean的整个生命周期负责,这一点我不太能理解,这样它占用的资源就很高昂了,那么prototype的好处是什么?要结束它的生命周围,要怎么做?而且,感觉request和prototype,除了prototype的生命周期不会被回调以外,它们的作用是不是一样?有什么具体的区别呢?
下边是在一个论坛中看到的答案 感觉还比较不错
request依赖于web应用,request就类似与你的servlet,多个用户访问一个servlet,当然是访问servlet的多个实例,prototype实际上是new出来的,你想想,你的bean里new了一个对象,为什么要让spring给你销毁。prototype没有什么好不好的,只不过它能做singleton所不能做得(创建多个实例),在web项目之外也可以做request所不能做得事。prototype的销毁你不必关心,调用它的bean销毁了,它也就销毁了。request是web里所特有的。
综合上边这个老哥给出的答案,我总结了以下几点
-
spring的controller默认是单例,原因有二:
(1)为了性能:单例不用每次都创建
(2)不需要多例:只要controller中不定义属性,那么单例完全是安全可用的,如果定义了,那单例肯定会出现竞争访问;非要定义,则通过注解@Scope("prototype"),将其设置为多例模式。
对于web项目,可以Controller类上加注解
@Scope("prototype")或@Scope("request"),对非web项目,在Component类上添加注解@Scope("prototype")。