0
点赞
收藏
分享

微信扫一扫

【Spring】Bean 的作用域和生命周期

独孤凌雪 2023-06-08 阅读 101


目录

Bean作用域问题引入

Bean的作用域

1. 单例作用域 (singleton)

2. 原型作用域 (prototype)

3. 请求作用域 (request)

4. 会话作用域 (session)

5. 全局作用域 (application)

6. HTTP WebSocket 作用域 (websocket)

解决上述问题

Spring 的主要执行流程 

Bean的生命周期



Bean作用域问题引入

假设有一个Bean对象,但是A用户在使用的时候将Bean对象的数据修改了,导致B用户在使用的时候发生了预期之外的逻辑错误

有一个对象为Animal

public class Animal {
    private String name;

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

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}

将该对象注入到Spring容器中

@Component
public class Animals {

    @Bean
    public Animal animal(){
        Animal animal = new Animal();
        animal.setName("老虎");
        return animal;
    }
}

A用户使用Bean对象时,进行了修改操作

@Controller
public class AnimalController1 {

    @Autowired
    private Animal animal1;

    public void getAnimal(){
        System.out.println("修改前的数据 = "+animal1);
        System.out.println("=====================");
        Animal animal = this.animal1;
        animal.setName("加菲猫");
        System.out.println("A修改后拿到的数据 = "+animal);
    }
}

B用户在去使用Bean对象

@Controller
public class AnimalController2 {
    
    @Autowired
    private Animal animal2;
    
    public void getAnimal(){
        System.out.println("B未修改拿到的数据 = "+animal2);
    }
}

打印A用户和B用户公共Bean的值

public class App {
    public static void main(String[] args) {
        //得到Spring上下文的对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //执行A用户的代码
        AnimalController1 animalController1 = context.getBean("animalController1",AnimalController1.class);
        animalController1.getAnimal();
        //执行B用户的代码
        AnimalController2 animalController2 = context.getBean("animalController2",AnimalController2.class);
        animalController2.getAnimal();
    }
}

打印结果:都打印了A用户修改后的值

 

出现上述结果的原因是Bean默认是单例作用域,也就是所有用户使用的都是同一个对象,但是我们想要的结果是公共Bean可以被用户在自己的类中修改,但是不能影响到其他类,那该如何做?

了解了下面Bean的作用域后,答案自然知晓

Bean的作用域

 Bean的作用域有六种:

前两种作用域是在普通的 Spring 项目中使用, 后四种作用域存在于 Spring MVC 项目中.(前四种要知道)

1. 单例作用域 (singleton)

2. 原型作用域 (prototype)

3. 请求作用域 (request)

4. 会话作用域 (session)

5. 全局作用域 (application)

6. HTTP WebSocket 作用域 (websocket)

解决上述问题

通过了解Bean的作用域,可以得到Bean的作用域为prototype,来解决上述问题

Bean作用域的设置方式:

  1. 直接设置: @Scope(“prototype”)
  2. 使用枚举设置; @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 

在存储Bean对象的时候添加上述注解,两种方式任选择一种

对上述代码进行修改:只需要在存储Bean时,添加上述两个注解中的一个即可

Spring 的主要执行流程 

主要执行流程: 

Bean的生命周期

Bean的生命周期就是一个Bean对象从诞生到销毁的过程

Bean的生命周期分为以下五个步骤:

1. 实例化Bean

2.设置属性

3. 初始化Bean

4.使用Bean

5. 销毁Bean

下面通过代码的方法来观察 Bean 的生命周期: 

【代码示例】

public class BeanLifeController implements BeanNameAware {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行各种通知:" + s);
    }
 
    /**
     * xml 中 init-method 指定的前置方法
     */
    public void initMethod() {
        System.out.println("执行 init-method 前置方法");
    }
 
    /**
     * 改用注解后的前置方法
     */
    @PostConstruct
    public void PostConstruct() {
        System.out.println("执行 PostConstruct 前置方法");
    }
 
    /**
     * 销毁前执行方法
     */
    @PreDestroy
    public void PreDestroy() {
        System.out.println("执行 PreDestroy 销毁方法");
    }
 
    public void use() {
        System.out.println("使用 bean - 执行 use 方法");
    }
}

 使用原始的 <bean> 标签设置 bean

<bean id="beanLife" class="controller.BeanLifeController"
     init-method="initMethod" scope="prototype"></bean>

启动类: 

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        // 根据 id 获取 bean 对象
        BeanLifeController controller =
                context.getBean("beanLife", BeanLifeController.class);
        // 使用 bean
        controller.use();
        // 销毁 bean
        context.destroy();
    }
}

 执行结果: 

具体流程如下图:​​​​​​

【问题】为什么【依赖注入DI】的执行时机要在 【Bean 的初始化之前】?

public class BeanLifeController implements BeanNameAware {
 
    // 依赖注入DI
    @Autowired
    private UserService userService;
 
    @Override
    public void setBeanName(String s) {
        System.out.println("执行各种通知:" + s);
    }
 
    // 初始化的前置方法
    @PostConstruct
    public void PostConstruct() {
        // 在初始化的前置方法中调用
        userService.doUserService();
        System.out.println("执行 PostConstruct 前置方法");
    }
}

上述代码在初始化的前置方法中使用注入的 Bean, 如果是先初始化 Bean,  就会导致空指针异常, 我初始化方法中需要使用到注入的 Bean , 那么一定是先执行【依赖注入】, 在执行【初始化】。

举报

相关推荐

0 条评论