![9f3121a84b921571474e01097f8856e1.png](https://i-blog.csdnimg.cn/blog_migrate/4ac7f8d11384f90ecfba7adb9c891657.jpeg)
AbstractBeanFactory中有一个doGetBean方法,spring容器中的bean都会走这个方法过,这个方法可以用于bean的BeanDefinition中信息来判断是单例还是多例,并创建返回:
![6ff5d6c850f6d9f8a9019913996121d8.png](https://i-blog.csdnimg.cn/blog_migrate/5489e74f1cff1ec1849244e93342952c.jpeg)
![d2012787d4987c5aae35d04b4cc7aa18.png](https://i-blog.csdnimg.cn/blog_migrate/35f8eb03aeed43ab93b9dd9310d2a83c.jpeg)
我们在 else if (mbd.isPrototype())处打上断点,最终发现基本没有bean走到这一步:
![89730031fa3343ba61ff41987d082f1c.png](https://i-blog.csdnimg.cn/blog_migrate/cdb872ad5a88f8835e99977420c633e8.jpeg)
然后我们在某个Bean上加上@Scope:
![031701da5f477f8d2ccc8f945cc0d1ed.png](https://i-blog.csdnimg.cn/blog_migrate/4b89e5d12e9ca2cf849ac3bd0e78be8c.png)
重新启动debug:
![4e95de14780cd63cacf9dfaacdd65e23.png](https://i-blog.csdnimg.cn/blog_migrate/d7a465a2599c2af19778ed7bd23dccf7.png)
====================
所以,问题来了:
- 为什么要设计成单例
- 单例的bean如何解决线程安全问题
对于第一点,可以理解为为了简化资源消耗,因为每有一个请求打到Controller层十,如果该Controller是原型,那么就会重新实例化一个对象,这样对于高并发的情况下消耗是相当大的,所以设计为单例,所有请求访问的都是同一个对象。
但是这样就会有一个缺点,那就是多线程的情况下如何解决线程安全问题,因为你都是访问的同一个controller对象啊,假如此时多个线程同时访问该controller的同一个方法(假设该方法对实例变量进行了操作),那不就有线程安全问题了吗?那你说可以在需要的方法上加上悲观锁不就好了,频繁的线程切换会消耗大量cpu时间,在高并发惜时如金的情况下显然不合适,所以可以采用以“空间”换“时间”的做法:
threadlocal
常见的有RequestContextHolder、TransactionSynchronizationManager等都是采用threadlocal,其中RequestContextHolder的作用是获取当前请求的上下文,比如request、session这些,在没有threadlocal的情况下,我们大概要写一个加了悲观锁的静态方法,方法内容是获取当前线程的request对象,然后当前线程作为key,request对象作为value存到一个全局map里面。
有了threadlocal之后,我只需将request对象set入当前线程thread的Threadlocal.threadlocalmap属性中,threadlocal实际上并没啥用,只是起到一个“钩子”的作用,存值的时候负责用set方法把request对象甩到当前线程的threadlocalmap的entry[]数组中,取值的时候负责将threadlocalmap的entry[]数组对应的值从当前线程中“钩”出来(是通过threadlocal来作为key从entry[]中存取值,所以叫钩子):
![1dee2cd76d04b9aaefb42992fd5cfba8.png](https://i-blog.csdnimg.cn/blog_migrate/11e2f71894ac0dc44c2e4ecb8e91ab65.jpeg)
除上图中的方法还有其他封装threadlocal的方法,这里就不细谈了。