单例的应用场景
如 Controller、service、dao,没必要每个请求都新建一个对象,既耗费CPU、又耗费内存
以下情况一般适合单例:
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 没有成员变量的类
- 频繁访问数据库或文件的类
- 其他要求只有一个对象的场景
多例的应用场景
有成员变量的 service
单例的问题
service
增加 name 成员变量,get、set方法
@Service
public class StudentSrvImpl implements IStudentService {
private String name;
public StudentSrvImpl(){
System.out.println("StudentSrvImpl构造方法"+this);
}
@Override
public List<Student> query() {
System.out.println("调用 StudentSrvImpl 的query()方法");
return null;
}
public void save(Student student){
System.out.println("调用 StudentSrvImpl 的save()方法");
System.out.println(student.toString());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
第一个Controller
@RestController
public class StudentCtrl {
@Autowired
private IStudentService studentSrv;
@RequestMapping("/test")
public JsonResult test() throws InterruptedException {
StudentSrvImpl imp=(StudentSrvImpl)studentSrv;
System.out.println(this.getClass()+"::设置名字为:李雷");
imp.setName("李雷");
Thread.sleep(5000);
System.out.println(this.getClass()+"::"+imp.getName());
return new JsonResult(0,"执行成功!");
}
}
第二个Controller
@RestController
public class StudentCtrl2 {
@Autowired
private IStudentService studentSrv;
public StudentCtrl2(){
System.out.println("StudentCtrl()构造方法");
}
@RequestMapping("/test2")
public JsonResult test() throws InterruptedException {
StudentSrvImpl imp=(StudentSrvImpl)studentSrv;
System.out.println(this.getClass()+"::设置名字为:韩梅梅");
imp.setName("韩梅梅");
System.out.println(this.getClass()+"::"+imp.getName());
return new JsonResult(0,"执行成功!");
}
}
执行
先访问 http://localhost:8080/test
,然后立即访问 http://localhost:8080/test2
执行流程:
-
第一个Controller先修改 service 的name为 李雷,然后延迟5秒
-
然后第二个Controller修改 service 的name为 韩梅梅,然后打印name值
-
最后,第一个Controller打印name值,此时显示的是 韩梅梅
总结
单例模式下,StudentSrvImpl 的成员变量会被多个Controller修改,此时会导致数据混乱
当有成员变量,而且成员变量的值会改变时,就不要使用单例模式,否则会互相干扰,此时应该用多例模式
解决
使用多例模式,在 service 加上 @Scope("prototype")
注解,如下:
@Service
@Scope("prototype")
public class StudentSrvImpl implements IStudentService {
}