Bean作用域和生命周期

1.通过一个案例来看Bean作用域的问题

假设在现有的一个公共的Bean,提供给A用户和B用户使用,然而在使用的途中A用户却“悄悄”修改了公共Bean的数据,导致B用户使用时发生了预料之外的逻辑错误。

1.1被修改的Bean案例

公共Bean:

@Component
public class UsersComponent {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("java");
        return user;
    }
}

A用户使用时,进行了修改操作:


@Controller
public class UserController1 {
    @Autowired
    private User user1;
    
    public User getUser1() {
        User user = user1;
        System.out.println("原来的Bean: " + user.toString());
        user.setName("悟空");
        System.out.println("后来的Bean: " + user.toString());
        return user;
    }
}

B用户再去使用公共的Bean的时候:

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Spring-config.xml");
        context.getBean("userController1", UserController1.class).getUser1();
        context.getBean("userController2", UserController2.class).getUser1();
    }
}

Bean作用域和生命周期_第1张图片

 1.2原因分析

Bean默认情况下是单例模式,所有人使用的是相同的对象,这就在使用的时候带了不便。

2.作用域定义

限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。

Bean的作用域是指Bean在Spring整个框架中的某种行为模式,比如单例模式,表示Bean在整个Spring中只有一份,它是全局共享的,那么当其他人修改了这个指以后,那么另一个人就读到了被修改的数据。

2.1Bean的6中作用域

Spring容器在初始化一个Bean的示例时,同时会指定该示例的作用域。Spring有6中作用域,最后4种是基于Spring MVC生效的:

  1. singleton:单例作用域
  2. prototype:原型作用域(多例作用域)
  3. request:请求作用域
  4. session:会话作用域
  5. application:全局作用域
  6. websocket:HTTP WebSocket作用域

后四种是Spring MVC种的值,在普通Spring项目中只有前两种。

2.2singleton

  • 描述:该作用域下的Bean在IoC容器中只存在一个实例:获取Bean和装配Bean都是同一个对象。
  • 适用场景:通常无状态的Bean使用该作用域。无状态表示Bean的属性不用更新。
  • 备注:Spring默认使用该作用域

2.3protorype

  • 描述:每次对该作用域下的Bean的请求都会创建新的实例:获取Bean以及装配Bean都是新的对象
  • 适用场景:通常有状态的Bean使用该作用域

2.4request

  • 描述:每次http请求会创建新的Bean实例
  • 适用场景:一次http请求和相应的共享Bean
  • 备注:限定SpringMVC中使用

2.5session

  • 描述:在一个http session中,定义一个Bean实例
  • 适用场景:用户回话的共享Bean,比如记录一个用户的登录信息。
  • 备注:限定在SpringMVC中适用

2.6application(了解)

  • 描述:在一个http servlet Context中,定义一个Bean实例
  • 适用场景:Web应用的上下文信息,比如:记录一个应用的共享信息。
  • 备注:限定SpringMVC中使用

2.7websocket(了解)

  • 描述:在一个HTTP WebSocket的生命周期中,定义一个Bean实例
  • 适用场景:WebSocket的每次会话中,保存一个Map结构的头信息将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean
  • 备注:限定Spring WebSocket中适用

2.8单例作用域VS全局作用域

  • singleton是Spring Core的作用域;application是Spring Web中的作用域
  • singleton作用于IoC的容器,而application作用于Servlet的容器

2.9设置作用域

使用@Scope标签就可以来声名Bean的作用域,比如设置Bean的作用域,如下代码:

@Component
public class UsersComponent {
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("java");
        return user;
    }
}

@Scope标签可以修饰方法也可以修饰类,@Scope有两种设置方式:

  • 直接设置值:@Scope("prototype")
  • 使用枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTORYPE)

3.Spring执行流程和Bean的生命周期

3.1Spring执行流程

Bean的执行流程(Spring执行流程):启动Spring容器 -> 实例化Bean(分配内存空间从无到有)  -> Bean注册到Spring中(存操作) -> 将Bean装配到需要的类中(取操作)

Bean作用域和生命周期_第2张图片

 3.2Bean的生命周期

所谓的生命周期就是指一个对象从诞生到销毁的整个生命过程,我们把这个过程称为对象的生命周期。

Bean的生命周期分为5部分:

  1. 实例化Bean(为Bean分配内存空间)
  2. 设置属性(Bean注入和装配)
  3. Bean初始化
    1. 实现各种Aware通知的方法,如 BeanNameAware
    2. 执行BeanPostProcess初始化前置方法
    3. 执行@PostConstruct初始化方法,依赖注入操作之后被执行
    4. 执行自己指定的init-method方法
    5. 执行BeanPostProcessor初始化后置方法
  4. 使用Bean
  5. 销毁Bean

3.3实例化和初始化的区别

实例化和属性设置是Java级别的系统“事件”,其操作过程不可人工干预和修改;而初始化是给开发人员提供的,可以在实例化以后,类加载完成之前进行自定义“事件”处理。

4.总结

本文主要介绍了Bean的6个作用域:

  • singleton:单例作用域
  • prototype:原型作用域
  • request:请求作用域
  • session:回话作用域
  • application:全局作用域
  • websocket:HTTP WebSocket作用域

同时介绍了Spring的执行流程和Bean生命周期

你可能感兴趣的:(java,spring)