@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Demo demo(){
return new Demo();
}
复制代码
若是使用XML来配置bean,我们可以使用<bean>
元素的scope属性,配置bean的作用域
复制代码
我列举的这几个例子都是声明的prototype(原型)作用域,每次注入或从Spring应用上下文检索该bean时,都会创建新的实例,这样我们每次操作都会得到我们自己的bean实例。
会话作用域和请求作用域
===========
以上我们介绍了,单例与原型作用域的声明。但是在有的场景我们使用前两种作用域,并不合适。例如,在Web应用中实现购物车。
若是使用单例作用域,我们每次往购物车添加商品都是往一个购物车添加,这并不合理。若是使用原型,我们每次添加商品都会创建一个购物车,下次获取购物车也不是同一个,也不合理。按照购物车bean来说,会话作用域是最适合的,为每个用户会话创建一个购物车。
声明会话作用域bean
指定会话作用域bean,我们也是使用@Scope注解。除了给@Scope注解配置value属性,我们还配置了 proxyMode 属性(代理模式),ScopedProxyMode.INTERFACES
表示基于接口的动态代理模式(jdk)。配置 proxyMode(代理模式)可以帮我们解决会话作用域bean注入到单例bean所遇到的问题。
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,//常量值为session
proxyMode = ScopedProxyMode.INTERFACES)//基于接口实现的代理模式
public class Demo { … }
复制代码
我们以商店与购物车为例,商店 StoreService(单例bean),购物车 ShoppingCart(会话bean)。在 StoreService的setShoppingCart()方法注入 ShoppingCart bean。
@Component
public class StoreService {
/** 购物车 */
private ShoppingCart shoppingCart;
@Autowired
public void setShoppingCart(ShoppingCart shoppingCart){
this.shoppingCart = shoppingCart;
}
}
复制代码
因为 StoreService 是一个单例bean,会在Spring应用上下文加载时创建。在创建时,Spring会试图将 ShoppingCart 注入到 setShoppingCart()方法。但是 ShoppingCart 是会话作用域的bean,此时并不存在。只有当用户进入系统,创建会话之后,才会出现 ShoppingCart 实例。
那么问题来了,系统中会存在多个 ShoppingCart,每个用户进入系统,创建会话后,都会产生一个 ShoppingCart。但是 StoreService 是单例的,只能注入一个,我们并不想注入一个固定的 ShoppingCart 实例到 StoreService 中。我们所希望的是,当 StoreService 处理购物车时,注入的 ShoppingCart 恰好是当前会话所对应的那一个。
其实Spring并不会将 ShoppingCart bean 注入到 StoreService 中,而是注入一个 ShoppingCart bean 的代理。这个代理会暴露与 ShoppingCart 同样的方法,所以 StoreService 会将他看做一个购物车。当 StoreService 调用 ShoppingCart 的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的 ShoppingCart bean。
proxyMode属性设置
-
若 ShoppingCart 是个接口,其实现类 @Scope的 proxyMode 属性设置为
ScopedProxyMode.INTERFACES
,表示这个代理要实现 ShoppingCart 接口,并将调用委托给实现bean。该代理模式也是最为理想的。 -
若 ShoppingCart 是个类,Spring就没办法创建基于接口的代理,这时,他必须使用CGLib来生成基
【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
于类的代理。将proxyMode属性设置为ScopedProxyMode.TARGET_CLASS
,Spring会生成基于目标类扩展类的方式创建代理。
以上我们了解了会话作用域可能产生这类问题,同样的请求作用域bean也会面临相同的装配问题。因此,请求作用域的bean也应该使用作用域代理的方式注入。
如图:作用域代理延迟注入请求和会话作用域的bean
XML中声明作用域代理
如果不使用@Scope注解声明作用域代理,我们还可以使用XML文件来声明。 在XML中作用域可通过scope属性直接声明,但是声明作用域代理需要使用Spring aop 命名空间的元素<aop:scoped-proxy />。 <aop:scoped-proxy />默认采用CGLib方式创建目标类的代理。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns=“http://www.springframework.org/schema/beans”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:aop=“http://www.springframework.org/schema/aop”
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/beans/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id=“cart”
class=“com.abiao._03_scope.ShoppingCart”
scope=“session”>
<aop:scoped-proxy />
复制代码