Spring教程11-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配置
@Configuration
public class BeanConfig {
	public static Logger logger = LoggerFactory.getLogger(BeanConfig.class);
	//定义BeanPrototypeScope为一个Bean,所用域为原型
    @Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public BeanPrototypeScope beanPrototypeScope() {
        return new BeanPrototypeScope();
    }
	//定义UseProtoBean为一个Bean,并注入BeanPrototypeScope的实例
    @Bean
    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);
    @Test
    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使用

@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public ShoppingCar shoppingCar() {
	return new ShoppingCar();
}

定义会话作用域的方式和原型作用域是一致的,都是使用@Scope,但唯一不同的地方是,在定义会话作用域时我们指定了代理模式。
proxyMode = ScopedProxyMode.INTERFACES,这段代码表示ShoppingCar需要被代理,且这个代理要实现ShoppingCar的接口,并委托给它的实现类。那么为什么要定义这个代理规则呢?先看一段代码:

@Bean
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就是一个具体的类并没有实现购物车的标准接口,此时的代理模式又改如何调整呢?

@Bean
@Scope(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”公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值