此处仅是问题的简单描述,并没有按照规范整理,请大家谅解,这是我在遇到这样问题时,随手按照自己的想法记录下了自己的心得,看着有些乱,不过全是干货,希望谅解;
//在springboot 整合rabbitmq下 rabbitTemplate 默认是单例形式
如果仅是发送队列和接受队列消息 该单例模式就足够使用了
如果想要 对于 发布端进行消息推送确认,那么单例模式是无法满足的
如果我们有多个队列,并需要对每个队列发送是否成功的消息进行确认
这种情况下,如果是单例模式,那么整个项目中,仅有个一confirm 和 returncallback 实现接口
对于所有的队列消息的确认也只能在这一个接口中得到回复,这样就很难辨别确认的消息响应是具体哪个队列的。
所以这样的情况下,单例是无法满足的,因此需要使用多例模式
scope
prototype 多例模式也会出现一个问题
问题描述
1 在非指直接请求层 如 server层 ServerImp 使用多例 @Scope("prototype")
如果在 A 和 B 中分别引用了
A{
@Autowired
ServerImp sA;
}
B{
@Autowired
ServerImp sB;
}
2 此时A和B 都是默认的单例形式作为controller层
对于A在spring容器启动的时候,会创建一个A的实例,此时也会创建一个ServerImp的实例 sA
对于B在spring容器启动的时候,会创建一个B的实例,此时也会创建一个ServerImp的实例 sB
sA != sB
但由于a和b仅有一个实例,所以,每次请求使用它们的时候,它们并不会去创建一个新的实例
那么此时在a和b中注入的 sA和sB也只是在不同的地方具有不同的实例,但是相对于a和b而言,它们只有一分
所以,如果在sA和sB中创建了共享变量,那么这时候就会出现非线程安全问题。
单例模式
1 controller server ...等等 在spring容器启动的时候就会初始化一个单一实例
3 scope("prototype") 的时候位置不同,那么他被初始化和使用的时候也不一样
1 使用在controller层的情况下,他是在每次请求到该controller的时候,进行一个实例化,就是创建一个新的controller对象
在该请求结束后,该controller的实例就会被销毁,下次再有新的请求进来,就会再次创建一个新的实例
//使用该注解,标识,在类被实例化后(即调用构造方法后),调用下面的方法
@PostConstruct
public void init(){
//此处每次有新的请求进来的时候,都会实例化一个新的controller对象
System.out.println("ProductListController-->init:,this:"+this);
}
2 server层使用多例的注解,这个地方会出现很多的坑,这个也是我们需要特别注意的
首先我们说在默认的单例情况下
2.1 在controller层 使用@Autowired 来注入一个server层的实例
2.1 此时spring容器为我们怎么处理的?
2.1.1 在创建了一个server单实例后,在引入了该controller的地方,setter到该server的属性上
此时如果有3个或者n个controller同时 @Autowired Server 该server,那么这n个controller同时拥有同一个server对象
此时我们说下多例模式下
1 controller层是单例
server层使用多例模式
2 此时在3个controller中
//创建三个controller
controller_01{
@Autowired
Server server;// 01中的server对象的实例 地址 Server@server01
}
controller_02{
@Autowired
Server server // 02中的server对象的实例 地址 Server@server02
}
controller_03{
@Autowired
Server server // 03中的server对象的实例 地址 Server@server03
}
.....
//server层
@Service
@Scope("prototype")
Server{
}
我们会发现,在三个controller层注入的server的实例是不一样的。也就是体现了多例
但是我们需要注意:server的多例是相对于每个不同引入的地方而言的
其实我们会发现,在spring容器初始化的时候,他会为每一个controller中的server实例化一个对象
这个实例化对象,就被绑定到了这个controller上了,意思就是(如论你有n多个请求,都请求到同一个controller上
,此时这n个请求使用的server对象是同一个。),但是在不同的controller里的server是不一样的。
同理,如果我们的controller是prototype多例的,server也是多例的,这时候的情况和上面也是类似的
controller多例 是在每次请求的时候创建一个新的实例,
但是server不一样,如果在controller里面,使用@Autowired server进行了注入,该server的实例并不会在每次请求到
该位置的时候才进行实例化的,仍然是在spring容器初始化的时候就一样完成了对每个controller的注入
这里可能有点绕,下面给出伪代码
//controller——01类 多例
//在每次新的请求时创建一个新的实例
@Scope("prototype")
class controller_01{
@Autowired
Server server01 //spring容器初始化的时候为其注入了一个 Server@server01
}
//controller——02类 多例
//在每次新的请求时创建一个新的实例
@Scope("prototype")
class controller_02{
@Autowired
Server server02; //spring容器初始化的时候为其注入了一个 Server@server02
}
//在spring容器初始化的时候,就为存在@Autowired server注解的类注入了一个新的实例
@Scope("prototype")
class Server{
}
-------------
此时有3个请求进入了controller_01中,那么内存中将会创建三个新的实例
controller_01@001
controller_01@002
controller_01@003
这三个新的实例他们共享server01中的地址Server@server01,也就是他们持有同一个server01,
因为,该server01在spring容器初始话的时候就已经完成了实例化
同理,其他也是这样
探讨一个问题
此时我们就可以利用上面的原理,来解决一些非线程安全变量共享问题
描述:
有些时候,我们会在server层的类中定义一些私有属性,这些属性会被作为该类的全局变量来使用,而且该属性是一个动态赋值
如果向上面那样,虽然server在被多例后,在不同的controller类下具有不同的实例,但是,对于同一个controller类而言,他们
持有同一个server对象,也就是该属性是共享的,随时可能被另外的请求给修改。
这时候我们的server中的该属性是非线程安全的。这也是我们平时很少用的情况,严格来说也不符合spring的初衷
如果非要这样时候怎么办,那就不要使用@Autowired 这种方式去获取server的实例,这种获取其实也是单例的。
所以我们要想办法,在每次请求的时候,都要获取一个新的server实例,这样才可以保证变量线程安全
@Autowired
ApplicationContent applicationContent;
我们可以通过上下文容器在需要的时候主动去获取,
Server ser = applicationContent.getBean(Server.class);
这样每次获取到的ser都是一个新的实例。
这样就解决了多实例的问题