@controller 加上注解后,就会变为一个控制器,类似的还有@service @common .................... 加上后,会被纳入到spring的容器中,也就是单例容器中,这样所有的bean也就都是单例的,那么spring中类似的类,是如何保证线程安全的呢?
其实他们都不能保证线程安全,他们仅仅是保证了他们为单例对象,减少了对象的创建;
我们平时用的时候,很少会考虑到线程安全的问题的原因是: 我们都是用他们的方法,而不是用他们的成员变量;
例如在控制器中生命一个 变量 int = 1; 然后在方法中加入一个 i++; 返回i的值,每次调用都会返回不同的值;
如果非要加成员变量也不是不行; 有如下方法
1 设置该类非默认模式(默认就是单例) ,改为property 原型模式,这样每次都会创建一个新的类,没这么玩的
2 使用threadlocal 来设置变量,这样以空间换时间的方式,就可以保证线程安全
平时使用中,我们通常不会选择在类似的类中添加成员变量做运算; 那么spring是如何加载这些类的呢?
一般加载这些类分为三个过程, 实例化>>>依赖注入>>>初始化, 我们在平时使用中,尤其是service尤其明显,我在serviceA中 注入了serviceB,后来又在serviceB中注入了serviceA, A依赖B;B依赖A; 此为循环依赖,但是我们发现 使用过程中不会有任何问题为啥呢??
首先这种注入的方式是注解注入,我们知道注入的方式还有另外两种: 构造注入,setter 注入;spring解决循环依赖的方法并不能解决构造注入这种循环依赖;
解决方法大概是: 实例化A的时候,把A 放入缓存中,然后发现需要B,那么它去实例化B,然后把B放入缓存中,然后B发现需要A,怎么拿到A呢,先去缓存找,找不到就去实例化A,然后发现在缓存中找到了A ,那么拿来直接用,然后B注入A完成,B初始化完成,完成后,返回到A的依赖注入环节,A也注入完成,然后A也完成初始化;
其中 缓存是 getEarlyBeanReference 也叫做二级缓存,咱们平时说的sping容器就是一级缓存,三级缓存是singletonFactories,它其实是一个创建单例的对象的工厂,当从三级缓存获取到对象后,那么就会从三级缓存中移除,然后放入到二级缓存中,也就是一个半成品,这个半成品的作用就是告知其他依赖当前实例的对象,我已经在创建中,可以直接用,为了解决循环依赖,二级三级缓存缺一不可;三级缓存存在的意义是,防止多个依赖同一个实例的时候,创建不同的对象出来