springboot单例模式与线程安全问题踩的坑
z在线翻译最近有客户反映,使⽤公司产品时,偶尔会存在崩溃情况,⾃⼰测试⽆问题,然后去查⽇志,是报空指针。于是顺藤摸⽠ 往上找,好嘛,之前的开发使⽤了成员变量,感觉问题就是在这⾥了,因为众所周知,springboot 采⽤的是单例模式,所以,使⽤成员变量时⼀定要谨慎。下⾯上⼀张该类的截图:打字机字体
⼤家可能看到了,该类上⾯加上了@Scope("prototype") 注解,该注解的作⽤是将该类变成多例模式。讲道理因为变为了多例,应该不会有线程问题了。
我先说下我这边的⼀个代码环境,上⾯⼤家看到的BaController这个类⾥⾯有个init⽅法,会在继承它的类的所有⽅法前执⾏。战胜自我
公输 翻译北京德威国际学校使⽤的是@ModelAttribute注解,这个注解的意思是,在该controller的所有⽅法前执⾏,意在初始化,我猜测之前的同事应该是为了获取相同的⼀些参数,抽调出来做⼀个⽗类,随着迭代,别的同事为了⽅便,拿来就⽤,导致很多controller继承了该类。
@Scope("prototype")注解:⼤家设想⼀下,若⽗类加了@Scope("prototype")注解,⼦类controller并
没有加该注解,会怎样呢?该注解是否还有意义?再⽐如,我在某rvice上加上@Scope("prototype")注解,但调⽤的controller没有加@Scope("prototype")注解,那么会出现什么样的结果呢?⼤家可以去测试⼀下,测试⽅法也很简单,就是在对应的⽗类或rvice的⽆参构造⽅法⾥打印该类的地址。
ale
下⾯说下我的测试结果:先说⽗类上加了@Scope("prototype")注解,⼦类上没有加这种情况。结果是,同⼀⼦类继承的为同⼀⽗类,不同⼦类继承为不同⽗类。理解⼀下,很简单,因为springboot为单例模式,所以⼦类为单例,那么只有⼀个⼦类,⽗类肯定是⼀样的。所以,不同线程过来使⽤的为同⼀变量,就会有问题。北京电脑培训
骚体同理:在rvice上标注@Scope("prototype")注解,那在同⼀个controller⾥,该rvice还是同⼀个,也就是说还是单例的,在不同的controller⾥ 是不同的。测试⽅法同上。
现在说下解决⽅法:1、是在继承该controller的⼦类上都加上@Scope("prototype")注解。这样做的好处是简单。坏处也同样明显,因为是多例的,那么就会产⽣⼤量的实体类,占⽤⼤量内存,若是回收不及时,有可能会出现内存溢出。
2、是将变量私有化,⽐如使⽤线程变量,对变量加锁等,技术上会复杂⼀些,⽽且调试不太好调试。说不定那些地⽅就会出现问题,毕竟是⽼代码。莫言的获奖作品
trio
3、将该类转换为拦截器,将变量放⼊request⾥,⽤的时候取出来。