Bean Scopes,Spring Bean的作用域,也就是Spring创立的Bean实例的失效范畴。
Spring提供了如下6中不同的作用域:
- singleton: 单例Bean,每一个Spring IoC容器中只有一个Bean实例,这是Bean的默认作用域。
- prototype:原型Bean,每申请一次就生成一个新的Bean实例
- request:仅对 web-aware Spring ApplicationContext失效,每一次HTTP申请生成一个Bean实例。
- session: 仅对 web-aware Spring ApplicationContext失效,每一个HTTP session对应一个Bean 实例。
- application: 仅对 web-aware Spring ApplicationContext失效,ServletContext生命周期对应一个Bean实例。
- websocket:仅对 web-aware Spring ApplicationContext失效,websocket申请对应一个Bean实例。
其中有4个是和web-aware Spring ApplicationContext相干的,也就是和web利用相干,最罕用的是singleton和prototype两种。
单例Bean
singleton,单例Bean,是Spring默认的Bean作用域,咱们在配置Bean的时候如果没有指定作用域,那么他的作用域就是singleton。
Spring官网的一张图高深莫测的讲清楚了单例Bean的含意:
定义了一个id为accountDao的Bean,被其余3个Bean援用。如图,咱们在定义accountDao的时候没有指定作用域,默认的,他的作用域就是singleton,单例Bean。那么,在整个Spring IoC容器中就只创立一个accountDao的Bean实例,3个援用类援用的其实是同一个accountDao实例。
这种状况系按咱们肯定要留神accountDao的线程平安问题!应用单例Bean的时候你肯定要留神线程平安问题,尽可能的防止在单例Bean中应用属性对象,或者你十分明确的晓得该属性不须要关注线程安全性!
接下来思考一个问题:定义为单例Bena就肯定示意整个Spring IoC容器中只有该类的一个对象吗?
答案显示是No,你当然能够在Spring IoC容器中为某一个类创立多个单例对象。
原型Bean
原型Bean指的是Spring IoC容器中会有多个Bena实例,利用没申请一次,Spring IoC容器就会生成一个新的Bean实例返回。
如图,accountDao定义为原型Bean,被其余3个Bean援用,不论其余3个Bean是单例还是原型Bean,Spring IoC注入的accountDao都会是3个不同的对象。
原型Bean在申明的时候须要显式指定scope:
<bean id="dependencyB" class="springTest.DependencyB" scope="prototype"> </bean>
也通过注解形式指定scope:
@Component@Scope("prototype")public class DependencyB implements IDependencyB{ @Autowired //@Qualifier(value="dependencyA3") private IDependencyA dependencyA; public static void main(String[] args) { System.out.println("I am a new comer...hello world "); } @Override public void testB(){ System.out.print("This is DependencyB's test and ..."); dependencyA.test(); }
启动类加测试代码:
public class App { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("Now start to getbean..."); DependencyB dependencyB = applicationContext.getBean(DependencyB.class); dependencyB.testB(); System.out.println(dependencyB); dependencyB=applicationContext.getBean(DependencyB.class); System.out.println(dependencyB); }}
执行启动类:
Now start to getbean...This is DependencyB's test and ...I am DependencyA test...springTest.DependencyB@53ca01a2springTest.DependencyB@358c99f5
能够看到两次获取DependencyB,失去的是不同的对象。
原型Bean的非凡之处还在于:创立的时候交给了Spring IoC,然而Spring IoC并不负责销毁。然而好消息是,因为原型Bean的特殊性,所以,Spring IoC容器也没有必要在每次创立之后缓存该实例,Spring IoC容器只须要new出一个对象(其实不是New的,是通过反射机制或者CGLIB创立的)间接返回给利用就OK,所以,也就不会导致内存透露,
单例Bena和原型Bean的依赖
如果一个原型Bean要依赖一个单例Bena,其实是没有任何问题的,因为依赖注入是产生在Bean实例化之后的属性赋值阶段,原型Bean在每次申请的时候都会进行一次属性赋值、都会执行一次依赖注入,单例Bean depencyA会注入到原型Bean dependencyB中,不会存在任何问题。
其实咱们下面的例子,原型Bean dependencyB就是依赖单例Bean dependencyA的,咱们把DependencyB的test办法稍加革新、打印一下依赖对象dependencyA:
@Override public void testB(){ System.out.print("This is DependencyB's test and ..."+dependencyA); dependencyA.test(); }
执行启动类:
Now start to getbean...This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...springTest.DependencyB@358c99f5This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...springTest.DependencyB@3ee0fea4
咱们会发现两次获取到的尽管是不同的dependencyB对象,然而他们的依赖对象dependencyA却是雷同的对象,因为dependencyA是单例Bean。
然而反过来就不是这么回事了:如果一个单例Bean依赖一个原型Bean会怎么样?
为此,咱们把上例中的DependencyB批改为单例Bean,把DependencyA批改为原型Bean,做一下测试。
只须要把@Scope("prototype")注解从DependencyB类转移到DependencyA上即可。
运行启动类:
Now start to getbean...This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...springTest.DependencyB@358c99f5This is DependencyB's test and ...springTest.DependencyA@53ca01a2I am DependencyA test...springTest.DependencyB@358c99f5
咱们发现尽管定义DependencyA为原型Bean,然而两次获取到的DependencyB的依赖对象DependencyA是同一个对象。
这也不难理解,Bena的注入是在Bean实例化之后的属性赋值阶段解决的,单例Bean只实例化一次、属性负责也只产生一次,所以,依赖对象也只能被注入一次,尽管属性是原型Bean,因为只能被注入一次所以也就只能是同一个对象了。
其实倡议最好不要在单例Bean中援用原型Bean,如果非要这么做的话,Spring也提供了一种解决办法:Method Injection,能够采纳xml配置或注解@Lookup实现,个人感觉利用很少,临时疏忽。
上一篇 Spring FrameWork从入门到NB - 主动拆卸