最近在项目中遇到了一个bug,在解决bug的过程中,发现自己以往对于@Scope
注解的理解一直存在问题,甚至是有些错误的,通过排查项目中bug以及写demo、查阅资料,对于@Scope
有了新的认识。
先通过注解的javadoc,可以了解到,@Scope
在和@Component
注解一起修饰在类上,作为类级别注解时,@Scope
表示该类实例的范围,在和@Bean
一起修饰在方法上,作为方法级别注解时,@Scope
表示该方法返回的实例的范围。
对于@Scope
注解,我们常用的属性一般就是:value
和proxyMode
,value
就是指明使用哪种作用域范围,proxyMode
指明使用哪种作用域代理。
@Scope
定义提供了的作用域范围一般有:singleton
单例、prototype
原型、request
web请求、session
web会话,同时我们也可以自定义作用域。
- 作用域范围
singleton
单例范围,这个是比较常见的,Spring中bean的实例默认都是单例的,单例的bean在Spring容器初始化时就被直接创建,不需要通过proxyMode
指定作用域代理类型。
prototype
原型范围,这个使用较少,这种作用域的bean,每次注入调用,Spring都会创建返回不同的实例,但是,需要注意的是,如果未指明代理类型,即不使用代理的情况下,将会在容器启动时创建bean,那么每次并不会返回不同的实例,只有在指明作用域代理类型例如TARGET_CLASS
后,才会在注入调用每次创建不同的实例。
request
web请求范围,(最近遇到的问题就是和request
作用域的bean有关,才发现之前的理解有偏差),当使用该作用域范围时(包括下面的session
作用域),必须指定proxyMode
作用域代理类型,否则将会报错,对于request
作用域的bean,(之前一直理解的是每次有http请求时都会创建),但实际上并不是这样,而是Spring容器将会创建一个代理用作依赖注入,只有在请求时并且请求的处理中需要调用到它,才会实例化该目标bean。
session
web会话范围,这个和request
类似,同样必须指定proxyMode
,而且也是Spring容器创建一个代理用作依赖注入,当有会话创建时,并且在会话中请求的处理中需要调用它,才会实例话该目标bean,由于是会话范围,生命依赖于session。
- 作用域代理
如果指定为proxyMode = ScopedProxyMode.TARGET_CLASS
,那么将使用cglib代理创建代理实例;如果指定为proxyMode = ScopedProxyMode.INTERFACE
,那么将使用jdk代理创建代理实例;如果不指定,则直接在Spring容器启动时创建该实例。而且使用代理创建代理实例时,只有在注入调用时,才会真正创建类对象。
除了上述作用域范围,Spring也允许我们自定义范围,主要操作为:
- 先实现
Scope
接口创建自定义作用域范围类 - 使用
CustomScopeConfigurer
注册自定义的作用域范围
后面写了一个例子实践一下,自定义了一种同一分钟的作用域范围,即同一分钟获取的是相同实例。
首先自定义作用域范围类TimeScope
:
1 | 4j |
Scope
接口提供了五个方法,只有get()
和remove()
是必须实现,get()
中写获取逻辑,如果已有存储中没有该名称的bean,则通过objectFactory.getObject()
创建实例。
然后注册自定义的作用域范围:
1 |
|
然后注入调用timeScopeBean
,同一分钟内重复调用,使用相同实例,不同分钟将创建新实例。
关于@Scope
新的理解认识就这些了,以后再有新理解再补充吧 (⊙﹏⊙)b