8. Spring Bean 作用域

8. Spring Bean 作用域,第1张

来源说明
sigleton默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例
prototype原型作用域,每次依赖查找和依赖注入生成新 Bean 对象
request将 Spring Bean 存储在 ServletRequest 上下文中
session将 Spring Bean 存储在 HttpSession 中
application将 Spring Bean 存储在 ServletContext 中

主要是 Singleton 和 Prototype 这两种,其余的是在后续版本中才加入的,并且在 BeanDefinition 中也只有 sigleton 和 prototype 两种作用域的定义

8.1 Singleton

在一定范围内是唯一的,Spring 中就是在一个 BeanFactory 内部是唯一的

8.2 Prototype
@Configuration
@PropertySource("default.properties")
public class BeanScopeDemo {
​
    @Value("${usr.name:default}")
    private String name;
​
    @Value("${usr.age:28}")
    private int age;
​
    @Bean
    public User singletonUser() {
        return new User(this.name, this.age);
    }
​
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User prototypeUser() {
        return new User(this.name, this.age);
    }
​
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(BeanScopeDemo.class);
​
        context.refresh();
​
        System.out.println(context.getBean("singletonUser", User.class) == context.getBean("singletonUser", User.class));
        System.out.println(context.getBean("prototypeUser", User.class) == context.getBean("prototypeUser", User.class));
​
        context.close();
    }
}
​
public class User implements BeanNameAware {
​
    private transient String beanName;
    private String name;
    private int age;
​
    public User() {
    }
​
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    @PostConstruct
    public void init() {
        System.out.println("用户 Bean[" + beanName + "]初始化...");
    }
​
    @PreDestroy
    public void destroy() {
        System.out.println("用户 Bean[" + beanName + "]销毁...");
    }
​
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}
​
// 运行结果:
用户 Bean[singletonUser]初始化...
true
用户 Bean[prototypeUser]初始化...
用户 Bean[prototypeUser]初始化...
false
用户 Bean[singletonUser]销毁...

Spring 容器没有办法管理 protoType Bean 完整的生命周期,也没有办法记录实例的存在。销毁回调方法将不会执行,可以利用 BeanPostProcessor 进行清扫工作。

结论:

  1. Singleton Bean 无论依赖查找还是依赖注入,均为同一个对象,Prototype Bean 无论依赖查找还是依赖注入,均为新生成对象

  2. 如果依赖注入集合类型对象,Singleton Bean 和 Prototype Bean 均会存在一个且只有一个

  3. 无论是 Singleton 还是 Prototype Bean 均会执行初始化方法回调,不过仅 Singleton Bean 会执行销毁方法回调

8.3 Request

配置

  • XML——

  • Java 注解——@RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)

实现

  • API——RequestScope

对象会被代理,页面渲染时是新对象,通过 Model 传递给 JSP 的对象每次都是新的,但是代理对象是不变的

8.4 Session

配置

  • XML——

  • Java 注解——@SessionScope 或 @Scope(WebApplicationContext.SCOPE_SESSION)

实现

  • API——SessionScope

对象会被代理,页面渲染时是新对象,通过 Model 传递给 JSP 的对象在每次会话中都是相同的,不同会话中不同,但是代理对象是不变的

8.5 Application

配置

  • XML——

  • Java 注解——@ApplicationScope 或 @Scope(WebApplicationContext.SCOPE_APPLICATION)

实现

  • API——ServletContextScope

JSP EL 变量搜索路径 page -> request -> session -> application(ServletContext)

ApplicationContext 作用域其实就是将 Bean 放到 ServletContext 中进行存储,因此我们不必使用 Model 进行数据的传输,可以直接在 JSP 页面中使用 Application 作用域的bean

8.6 自定义 Bean 作用域
  • 实现 Scope

    • org.springfamework.beans.factory.config.Scope

  • 注册 Scope

    • API——org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope

    • 配置

      
          
              
                  
              
          
      
8.7 面试题

8.7.1 Spring 内建的 Bean 作用域有几种?

六种:sigleton、prototype、request、session、application 以及 websocket

从设计模式:单例和原生(原型)

从web模式:request、session、application、websocket

8.7.2 singleton Bean 是否在一个应用中是唯一的

不是,只是在一个 BeanFactory 中是唯一的,整个应用可能包含多个应用上下文(层次上下文),不同上下文中互不影响

同样的,一个静态字段在 JVM 中是否唯一?不是唯一的,静态字段相对于 ClassLoader 唯一,但是一个 JVM 进程可能包含多个 ClassCloader

8.7.3 application 作用域 Bean 是否能被其他方案替代

是可以的,实际上,application bean 与 singleton bean 没有本质区别,无非是在某处进行了存储,跟 Ioc 容器中的 bean 是同一个

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/800341.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存