spring scope spring scope
Spring定义的scope包含五种,singleton,prototype,request,session,global session,每种scope都有自己的使用范围以及使用场景。
Scopes a single bean definition to a single object instance per Spring IoC container.
个人理解:在Spring 的IOC容器中,全局只会存在一个对象实例。每次spring启动之后,就会生成一个,也只会有一个唯一的bean对象
Scopes a single bean definition to any number of object instances.
个人理解:有无数个对象的实例,也就是说每个上层在引用时,都是一个独立的对象
Scopes a single bean definition to the lifecycle of a single HTTP request; that is each and every HTTP request will have its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext
个人理解:在每个http请求的生成周期内,使用同一个对象的实例,不同的http之间使用的对象都是不同的实例。只有在使用了spring的ApplicationContext
的上下文才有效
Scopes a single bean definition to the lifecycle of a HTTP Session
,Only valid in the context of a web-aware Spring ApplicationContext
个人理解:在同一个session的生命周期内,使用同一个对象的实例
Scopes a single bean definition to the lifecycle of a global HTTP Session
. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext
个人理解:在同一个全局session的生命周期内,使用同一个对象的实例,跟上面的session的区别在于global,也就是全局,例如使用redis等分布式session的场景。
实例解读 为了方便更好的理解上面的内容,我们通过一个例子,来对不同范围下的bean进行一个测试
测试类图:
VehicleController:请求入口,用于验证不同情况下的结果输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RestController public class VehicleController { @Resource private TruckManager truckManager; @Resource private CarManager carManager; @GetMapping public void getRun (@RequestParam("type" ) int type) { if (type==1 ) { carManager.doRun(); } else { truckManager.doRun(); } } }
CarManager:调用WheelsManager,不做赋值
1 2 3 4 5 6 7 8 9 10 11 @Component @Slf 4jpublic class CarManagerImpl implements CarManager { @Resource private WheelsManager wheelsManager; @Override public void doRun () { log.info("CarManagerImpl doRun {}" ,wheelsManager.getWheelsCount()); } }
TruckManager:调用WheelsManager,给wheelsCount赋值为4
1 2 3 4 5 6 7 8 9 10 11 12 13 @Component @Slf 4jpublic class TruckManagerImpl implements TruckManager { @Resource private WheelsManager wheelsManager; @Override public void doRun () { wheelsManager.setWheelsCount(4 ); log.info("TruckManagerImpl doRun {}" , wheelsManager.getWheelsCount()); } }
WheelsManager:内部包含属性wheelsCount,并且提供get和set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component public class WheelsManagerImpl implements WheelsManager { private Integer wheelsCount; @Override public Integer getWheelsCount () { return wheelsCount; } public void setWheelsCount (Integer wheelCount) { wheelsCount = wheelCount; } }
测试方法 通过更改bean的scope的范围,接口调用分别传入1,2,来判断各种情况下wheelsCount的结果
singleton
验证不同bean,引入相同bean的场景
请求顺序:type = 1 → type = 2 → type = 1
1 2 3 4 5 6 7 2022-03-20 11:08:27.204 INFO 31046 --- [ restartedMain] c.iotxing.blog.test.TestDemoApplication : TestDemoApplication main 启动成功 2022-03-20 11:08:32.526 INFO 31046 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-03-20 11:08:32.526 INFO 31046 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2022-03-20 11:08:32.527 INFO 31046 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms 2022-03-20 11:08:32.552 INFO 31046 --- [nio-8080-exec-1] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl doRun null 2022-03-20 11:08:43.003 INFO 31046 --- [nio-8080-exec-3] c.i.b.t.manager.impl.TruckManagerImpl : TruckManagerImpl doRun 4 2022-03-20 11:08:49.842 INFO 31046 --- [nio-8080-exec-5] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl doRun 4
可以看出,在第一次调用时,因为wheelsCount没有赋值,所以打印出来的结果是null,第二次调用时,truckManager赋值了4,第三次调用时,carManager打印出来的也是4(上一次调用truckManager赋值的结果)
结论 :全局范围内,正常方式注入的bean只有一个实例,因此如果bean存在属性,不同的请求之间都是的更改都是可见的,属于线程不安全的场景。如果bean中没有属性,只有逻辑,也就不存在安不安全的问题了。
prototype 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Component @Scope ("prototype" )public class WheelsManagerImpl implements WheelsManager { private Integer wheelsCount; @Override public Integer getWheelsCount () { return wheelsCount; } public void setWheelsCount (Integer wheelCount) { wheelsCount = wheelCount; } }
spring默认的scope是singleton,更改为prototype的话,需要我们手动指定对应的scope
验证不同bean,引入相同bean的场景
请求顺序:type = 1 → type = 2 → type = 1
1 2 3 4 5 6 2022-03-20 11:31:07.252 INFO 31888 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-03-20 11:31:07.252 INFO 31888 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2022-03-20 11:31:07.253 INFO 31888 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms 2022-03-20 11:31:07.278 INFO 31888 --- [nio-8080-exec-1] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl doRun null 2022-03-20 11:31:16.509 INFO 31888 --- [nio-8080-exec-3] c.i.b.t.manager.impl.TruckManagerImpl : TruckManagerImpl doRun 4 2022-03-20 11:31:21.201 INFO 31888 --- [nio-8080-exec-5] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl doRun null
可以发现更改了scope之后,CarManager和TruckManager在初始化的时候,各自都创建了一个WheelsManager的实例出来
验证同一个bean,不同请求下的使用场景
针对单个CarManager,我们做一下改造,看一下prototype对单个bean的情况下,效果是怎么样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Component @Slf 4jpublic class CarManagerImpl implements CarManager { @Resource private WheelsManager wheelsManager; @Override public void doRun () { log.info("CarManagerImpl doRun {}" ,wheelsManager.getWheelsCount()); addPreWheel(); addWholeWheel(); } public void addPreWheel () { wheelsManager.setWheelsCount(wheelsManager.getWheelsCount()+2 ); log.info("CarManagerImpl addPreWheel {}" ,wheelsManager.getWheelsCount()); } public void addWholeWheel () { wheelsManager.setWheelsCount(wheelsManager.getWheelsCount()+10 ); log.info("CarManagerImpl addWholeWheel {}" ,wheelsManager.getWheelsCount()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 @Component @Slf 4jpublic class TruckManagerImpl implements TruckManager { @Resource private WheelsManager wheelsManager; @Override public void doRun () { wheelsManager.setWheelsCount(wheelsManager.getWheelsCount()+4 ); log.info("TruckManagerImpl doRun {}" , wheelsManager.getWheelsCount()); } }
上面我们增加了两个方法,调用addPreWheel的时候,会给wheelsCount+2,调用WholeWheel的时候,会给wheelsCount+10,我们验证下分多次调用时候的结果
验证顺序: type = 1 → type = 1 → type = 2
1 2 3 4 5 6 7 8 2022 -03 -20 11 :52 :56.917 INFO 32882 --- [nio-8080 -exec-1 ] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms2022 -03 -20 11 :52 :56.944 INFO 32882 --- [nio-8080 -exec-1 ] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl doRun 0 2022 -03 -20 11 :52 :56.944 INFO 32882 --- [nio-8080 -exec-1 ] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl addPreWheel 2 2022 -03 -20 11 :52 :56.944 INFO 32882 --- [nio-8080 -exec-1 ] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl addWholeWheel 12 2022 -03 -20 11 :52 :58.922 INFO 32882 --- [nio-8080 -exec-3 ] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl doRun 12 2022 -03 -20 11 :52 :58.926 INFO 32882 --- [nio-8080 -exec-3 ] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl addPreWheel 14 2022 -03 -20 11 :52 :58.926 INFO 32882 --- [nio-8080 -exec-3 ] c.i.b.test.manager.impl.CarManagerImpl : CarManagerImpl addWholeWheel 24 2022 -03 -20 11 :53 :01.678 INFO 32882 --- [nio-8080 -exec-5 ] c.i.b.t.manager.impl.TruckManagerImpl : TruckManagerImpl doRun 4
可以发现,相同的bean在不同的请求时候,使用的是同一个对象,因此第二次请求时候的值是在第一次的基础上累加的,不同的bean在使用时,使用的是不同的对象,因此TruckManager的初始值是4
结论:spring启动时,对于不同的类依赖的相同的bean,注入的时候是注入的同一个bean对象的不同实例。
其它 由于request,session和global session对验证起来要求较多,并且实际中这种场景我接触不多,因此不做专门的测试了