Spring5 Bean的作用域
Posted 冰点IT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring5 Bean的作用域相关的知识,希望对你有一定的参考价值。
1、什么是Bean的作用域
Bean的作用域也就是Bean的作用范围,Spring默认Bean的作用域是单例(Singletion),也就是说Bean在容器加载时只会被实例化一次,之后无论被注入多少次,都使用的是同一个实例。那么Spring还未我们提供了那些作用域呢?
单例(Singleton):在整个应用中,只创建一个Bean的实例。
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的Bean实例。
会话(Session):在Web应用中,Spring会为每一个回话创建一个bean的实例。
请求(Request):在Web应用中,Spring会为每一个请求创建一个Bean的实例。
应用(Application):在Web应用中,Spring会为每一个Servlet上下文创建一个Bean的实例。
WebSocket:Spring会为每个webSocket的生命周期创建一个Bean的实例。
2、作用域的使用
2.1、原型作用域的使用
基于JavaConfig:
//简单Java类
public class BeanPrototypeScope {
}
//BeanPrototypeScope被注入
public class UseProtoBean {
private BeanPrototypeScope beanPrototypeScope;
public UseProtoBean(BeanPrototypeScope beanPrototypeScope) {
this.beanPrototypeScope = beanPrototypeScope;
}
}
//JavaConfig配置
public class BeanConfig {
public static Logger logger = LoggerFactory.getLogger(BeanConfig.class);
//定义BeanPrototypeScope为一个Bean,所用域为原型
(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public BeanPrototypeScope beanPrototypeScope() {
return new BeanPrototypeScope();
}
//定义UseProtoBean 为一个Bean,并注入BeanPrototypeScope的实例
public UseProtoBean useProtoBean(BeanPrototypeScope beanPrototypeScope) {
logger.info("注入时BeanPrototypeScope的实例是:{}",beanPrototypeScope.toString());
return new UseProtoBean(beanPrototypeScope);
}
}
测试类:
public class TestBeanScope {
public static Logger logger = LoggerFactory.getLogger(TestBeanScope.class);
public static AnnotationConfigApplicationContext acac = new
AnnotationConfigApplicationContext(BeanConfig.class);
public void testPrototype() {
BeanPrototypeScope beanPrototypeScope = acac.getBean("beanPrototypeScope",
BeanPrototypeScope.class);
logger.info("通过上下文获取时BeanPrototypeScope的实例是:{}",
beanPrototypeScope.toString());
}
}
有原型作用域的特性可知,BeanPrototypeScope会在通过容器获取的时候实例化一次,然后在注入到UseProtoBean时再实例化一次,下面来看运行结果。
运行结果:
21:51:51,372 INFO main configuration.BeanConfig:30 - 注入时BeanPrototypeScope的实例是:com.icypt.learn.BeanPrototypeScope@4c5ae43b
21:51:51,413 INFO main learn.TestBeanScope:23 - 通过上下文获取时BeanPrototypeScope的实例是:com.icypt.learn.BeanPrototypeScope@6f3187b0
由运行结果可知,两次产生的实例确实各不相同。
基于XmlConfig使用:
<bean id="beanPrototypeScope" class="com.icypt.learn.BeanPrototypeScope" scope="prototype"/>
2.2、会话和请求作用域的使用
在Web应用中,如果能够实例化在会话和请求范围内共享的bean,那将是非常有价值的事情。例如,在某个网上商城应用中,我们肯定希望购物车这个Bean是在会话范围内共享的。假如它是一个单例的Bean且不做任何的硬编码,那岂不是每个用户都向同一个购物车添加商品了,假如它是一个原型的Bean,那么用户添加的商品,可能在其他地方就不可用了,那么就购物车这个Bean来说,把它定义为一个会话作用域的Bean是最合适不过的了。
基于JavacConfig使用
(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public ShoppingCar shoppingCar() {
return new ShoppingCar();
}
定义会话作用域的方式和原型作用域是一致的,都是使用@Scope,但唯一不同的地方是,在定义会话作用域时我们指定了代理模式。
proxyMode = ScopedProxyMode.INTERFACES,这段代码表示ShoppingCar需要被代理,且这个代理要实现ShoppingCar的接口,并委托给它的实现类。那么为什么要定义这个代理规则呢?先看一段代码:
public User user(ShoppingCar shoppingCar) {
return new User(shoppingCar);
}
代码中User是一个单例的Bean,但是依赖了ShoppingCar这个会话作用域的Bean,那么当Spring加载容器的时候,由于此时ShoppingCar还不会被实例,但是Spring又要建立其引用关系,由于每个User对应的ShoppingCar是不同的,此时Spring容器又无法判断要使用哪一个ShoppingCar,所以Spring只能将ShoppingCar的代理注入到User这个bean之中,对外暴露和ShoppingCar相同的方法,当User调用ShoppingCar的方法时,代理会对其进行懒加载解析并将调用委托给会话作用域内的ShoppingCar的实例来完成, 那么假如ShoppingCar就是一个具体的类并没有实现购物车的标准接口,此时的代理模式又改如何调整呢?
(value = WebApplicationContext.SCOPE_SESSION, proxyMode =
ScopedProxyMode.TARGET_CLASS)
public ShoppingCar shoppingCar() {
return new ShoppingCar();
}
如果ShoppingCar就是一个具体的类,则必须使用CGLIB来生成基于类的代理。
基于XmlConfig使用:
<bean id="shoppingCar" class="com.icypt.learn.ShoppingCar">
<!-- 默认是基于CGLIB代理的,也就是基于类的代理-->
<aop:scoped-proxy/>
</bean>
当然我们也可以设置为基于接口的代理:
<bean id="shoppingCar" class="com.icypt.learn.ShoppingCar">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
那么以上就是Bean作用域的全部内容了,application、websocket、request等作用域的使用方式和session的使用方式相同,都是需要创建代理的这里就不一一介绍了,下次我们讨论Spring的EL表达式。
关注我
冰点IT,探索最新开发技术,记录追求梦想的点点滴滴!
长按二维码,或点击最上方蓝色的 冰点IT 关注我,您的关注是对我最好的鼓励!
以上是关于Spring5 Bean的作用域的主要内容,如果未能解决你的问题,请参考以下文章
[Spring5]IOC容器_Bean管理_bean的作用域和bean的生命周期
Spring5学习笔记 — “IOC操作Bean管理(基于注解)”
Spring5学习笔记 — “IOC操作Bean管理(基于注解)”