Spring 基于注解设置 Bean 的作用域

Spring 中 Bean 的作用域

前言

在默认情况下,Spring应用上下文中所有的 Bean 都是作为以单例的形式创建的。也就是说,不管给定的一个 Bean 被注入到其他 Bean 多少次,每次所注入的都是同一个实例。
在大多数情况下,单例的 Bean 是很理想的方案,初始化和垃圾回收对象实例所带来的成本只留给一些小规模任务。在这些任务中,对对象的保持无状态并且在应用中反复重用这些对象可能并不合理。

Bean 的作用域

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

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

单例是默认的作用域。如果选择其他的作用域,要使用 @Scope 注解,他可以与 @Component 或者 @Bean 一起使用。

详细:

例如,如果你使组件扫描来发现和声明 Bean,那么你可以在 Bean 的类上使用 @Scope 注解,将其声明为原型 Bean :

@Component
@Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE)
public class BeanScope {

    private String name;

    public String getName() { return name; }

    public void setName(String name) { this.name = name;}
}

这里,使用 ConfigurableListableBeanFactory 类的 SCOPE_PROTOTYPE 常量设置类原型作用域。当然也可以直接使用 @Scope(“prototype”),但是使用 SCOPE_PROTOTYPE 常量更加安全并且不易出错。
下面两张图,一个是原型 Bean 模式下的一个是单例 Bean 模式下的不同输出结果。

原型 bean 作用域:

这里写图片描述

单例 bean 作用域:

这里写图片描述

会话/请求 作用域与上面的形式稍有不同。

会话:
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,
        proxyMode = ScopedProxyMode.INTERFACES)
public class BeanScope {
    …………
}
请求:
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST,
        proxyMode = ScopedProxyMode.INTERFACES)
public class BeanScope {
    …………
}

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

@Service
public class BeanServiceImpl {
    @Autowired
    private void setBeanScope(BeanScope beanScope) {
        this.beanScope = beanScope;
    }
}

看到上面的代码,因为 BeanServiceImpl 是一个单例的 Bean ,会在 Spring 应用上下文加载的时候创建,当他创建的时候,Spring 会试图将 BeanScope 的 Bean 注入到 setBeanScope() 方法中。 但是 BeanScope Bean 是会话(或请求)作用域的 Bean。此时并不存在。直到某个用户进入系统,创建会话之后,才会创建 BeanScope 实例。

另外,系统中将会有多个 BeanScope 实例:每个用户一个。我们并不想让 Spring 注入某个固定的 BeanScope 实例到 BeanServiceImpl 中。我们希望的是当 BeanServiceImpl 做业务逻辑的时候,他所使用的 BeanScope 实例恰好是当前会话(或请求)所对应的那一个实例。

Spring 并不会将实际的 BeanScope Bean 注入到 BeanServiceImpl 中,Spring 会注入一个到 BeanScope Bean 的代理。如图:
这里写图片描述

这个代理会暴露与 BeanScope 相同的方法。但是,当 BeanServiceImpl 调用 BeanScope 的方法时,代理会对其进行懒解析并调用委托给会话作用域内的真正 BeanScope Bean。

proxyMode 属性

如配置所示, proxyMode 属性设置成了ScopedProxyMode.INTERFACES ,表明这个代理要实现 BeanScope 接口,并将调用委托给实现的 Bean。

但是 BeanScope 不是接口是类(最好是接口 - -,上面只是为了演示)。Spring 就没办法创建基于接口的代理了。此时,他必须使用 CGLib 生成基于类的代理。所以, Bean 类型是具体类的话,我们必须要将 proxyMode 属性设置为 ScopedProxyMode.TARGET_CLASS 依次来表明要以生成目标类扩展的方式创建代理。


若博客中有错误或者说的不好,请勿介意,仅代表个人想法。
csdn博客:https://blog.csdn.net/LNView
本文地址:https://blog.csdn.net/LNView/article/details/80292107

有问题或者喜欢的欢迎评论。

转载请注明出处!!!!!!

参考资料
Spring 实战(第四版)

猜你喜欢

转载自blog.csdn.net/lnview/article/details/80292107