Spring学习总结(二)- Bean的作用域

bean的作用域

Spring定义了多种作用域,可以基于这些作用域创建bean,包括:

  • 单例(Singleton):在整个应用中,只创建bean的一个实例。
  • 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
  • 会话(Session):在Web应用中,为每个会话创建一个bean实例。
  • 请求(Rquest):在Web应用中,为每个请求创建一个bean实例。

单例是默认的作用域,在默认情况下,Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。

如果选择其他的作用域,要使用@Scope注解,它可以与@Component或@Bean一起使用。
例如将其声明为原型bean:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

使用ConfigurableBeanFactory类的SCOPE_PROTOTYPE常量设置了原型作用域
它可以简写为:

@Scope("prototype")

但是建议使用SCOPE_PROTOTYPE,因为常量更加安全并且不易出错。

如果想在Java配置中将Apple声明为原型bean,可以和和@Bean来配合使用。

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Apple apple(){
	return new Apple();
}

XML来配置bean的话也是可以的,可以使用元素的scope属性来设置作用域

单例和原型作用域讲解了,接下来学习会话和请求作用域

使用会话和请求作用域

架设在一个Web应用中,有一个收藏夹的功能,它能够把你看到喜欢的文章收藏到里面。
现在拿一个Bean来代表这个收藏夹,那么使用哪种作用域才更适合这个功能呢?

假设使用单例的话,那么会导致只有一个收藏夹,所有的用户都向这个收藏夹里面添加东西,这么做显然是不行的。

假设使用原型的话,那么在应用中某一个地方往收藏夹中添加文章,在应用的另外一个地方可能就不可用了。因为另外一个地方新注入或者重新通过Spring应用上下文获取的时候,都会创建一个新的bean实例,在这个应用中就代表创建了一个崭新的收藏夹

假设使用请求的话,那不是每次查询收藏夹中内容的时候,都会创建一个新的收藏夹Bean。这也显然不合适。

最后只剩下会话作用域

使用会话作用域

要指定会话作用域,我们可以使用@Scope注解,它的使用方式与指定原型作用域是大同小异的:

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public Favorites favorites { ... }

这里,将value设置成了WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。这会告诉Spring为Web应用中的每个会话创建一个 Favorites。这会创建多个 Favorites bean的实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个bean实际上相当于单例的。

要注意的是,@Scope同时还有一个proxyMode属性,它被设置成了ScopedProxyMode.INTERFACES。这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。

假设我们要将 Favorites bean注入到单例 WebsiteService bean的Setter方法中,如下所示:

@Component
public class WebsiteService {

    private Favorites favorites;

    @Autowired
    public void setFavorites(Favorites favorites) {
        this.favorites = favorites;
    }
}

因为 WebsiteService 是一个单例的bean(没有使用@Scope单独指定作用域,默认就是单例作用域),会在Spring应用上下文加载的时候创建。当它创建的时候,Spring会试图将 Favorites bean
注入到 setFavorites()方法中。但是 Favorites bean是会话作用域的,此时并不存在。直到某个用户进入系统,创建了会话之后,才会出现 Favorites 实例。

另外,系统中将会有多个 Favorites 实例:每个用户一个。我并不想让Spring注入某个固定的 Favorites 实例到 WebsiteService 中。我希望的是当 WebsiteService 处理收藏夹功能时,它所使用的 Favorites 实例恰好是当前会话所对应的那一个。

Spring并不会将实际的 Favorites bean注入到 WebsiteService 中,Spring会注入一个 Favorites bean的代理。这个代理会暴露与 Favorites 相同的方法,所以 WebsiteService会认为它就是一个收藏夹。但是,当 WebsiteService 调用 Favorites 的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的 Favorites bean。

如配置所示,proxyMode属性被设置成了ScopedProxyMode.INTERFACES,这表明这个代理要实现Favorites 接口,并将调用委托给实现bean。如果 Favorites 是接口而不是类的话,这是可以的(也是最为理想的代理模式)。

但如果 Favorites 是一个具体的类的话,Spring就没有办法创建基于接口的代理了。此时,它必须使用CGLib来生成基于类的代理。所以,如果bean类型是具体类的话,我们必须要将proxyMode属性设置为ScopedProxyMode.TARGET_CLASS,以此来表明要以生成目标类扩展的方式创建代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值