Bean 作用域和生命周期

1. 观察案例

User:

public class User {
    
    
    public int id;
    public String name;

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

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

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

UserBean(公共的Bean):

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBean {
    
    

@Bean(name = "user1")
    public User getUser(){
    
    
    User user = new User();
    user.setId(1);
    user.setName("张三");
    return user;
}
}

UserController1 在使用时,进行了修改操作:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController1 {
    
    

    @Autowired
    private User user1;
    public User getUser1(){
    
    
        User user = user1;
        System.out.println("userController1 修改之前的 User: "+user);
        user.setId(2);
        user.setName("李四");
        return user;
    }
}

UserController2 再去使用公共 Bean:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController2 {
    
    
    @Autowired
    private User user1;

    public User getUser2(){
    
    
        User user = user1;
        return user;
    }
}

打印 UserController1 和 UserController2 公共 Bean 的值:

import beans.UserController1;
import beans.UserController2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        
        UserController1 userController1 = context.getBean("userController1",UserController1.class);
        User user1 = userController1.getUser1();
        System.out.println("userController1 修改之后的 User: "+user1);

        UserController2 userController2 = context.getBean("userController2",UserController2.class);
        User user2 = userController2.getUser2();
        System.out.println("userController2 在 userController1 修改之后读出的 User: "+user2);
    }
}

结果:

在这里插入图片描述

原因:Bean 作用域:bean 在 spring 整个框架中,默认所有人使用同一个对象,作用域默认是singleton(单例模式)

2. 作用域定义

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

⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值

3. Bean 的 6 种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域.

  1. singleton:单例模式(默认)
  1. prototype: 原型模式(多例模式)
  1. request: 请求作用域(Spring MVC)
  1. session: 会话作用域(Spring MVC)
  1. application: 全局作用域(Spring MVC)
  1. websocket: HTTP WebSocket 作用域(Spring WebSocket)

注意: 后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种

3.1 singleton

描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。

场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新

备注:Spring默认选择该作⽤域

3.2 prototype

描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的对象实例。

场景:通常有状态的Bean 使⽤该作⽤域

3.3 request

描述:每次http请求会创建新的Bean实例,类似于prototype

场景:⼀次http的请求和响应的共享Bean

备注:限定SpringMVC中使⽤

3.4 session

描述:在⼀个http session中,定义⼀个Bean实例

场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息

备注:限定SpringMVC中使⽤

3.5 application

描述:在⼀个http servlet Context中,定义⼀个Bean实例

场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息

备注:限定SpringMVC中使⽤

3.6 websocket

描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bea实例

场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直WebSocket结束都是同⼀个Bean。

备注:限定Spring WebSocket中使⽤

单例作⽤域(singleton)和全局作⽤域(application)区别

1: singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;

2: singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器

4. 设置 Bean 作用域

1: 使用枚举设置(类似于枚举,是常量的方式):@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

2: 直接设置值: @Scope(“prototype”)

4.1 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

在这里插入图片描述

4.2 @Scope(“prototype”)

在这里插入图片描述

5. Bean 生命周期

所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期.

Bean 的⽣命周期分为以下 5 ⼤部分:

1: 实例化(给 bean 分配内存空间)

2: 设置属性(对象注入)

3: 初始化
a) 执行各种通知(执行各种 Aware)
b) 执行初始化的前置方法(BeanPostProcessor)
c) 执行构造方法,两种执行方式,一种是执行 @PostConstruct,另一种实质性 init-method
d) 执行初始化的后置方法(BeanPostProcessor)

4: 使用 Bean

5: 销毁 Bean(销毁容器的各种方法)
a) @PreDestroy
b) 重写 DisposableBean 接口方法
c) destroy-method

在这里插入图片描述

5.1 实例化和初始化的区别

实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈⼯⼲预和修改;⽽初始化是给开发者提供的,可以在实例化之后,类加载完成之前进⾏⾃定义“事件”处理

5.2 生命流程"案例"

Bean 的⽣命流程看似繁琐,但咱们可以以⽣活中的场景来理解它,⽐如我们现在需要买⼀栋房⼦,那么我们的流程是这样的:

  1. 先买房(实例化,从⽆到有);
  2. 装修(设置属性);
  3. 买家电,如洗⾐机、冰箱、电视、空调等([各种]初始化);
  4. ⼊住(使⽤ Bean);
  5. 卖出去(Bean 销毁)。

5.3 演示代码

import org.springframework.beans.factory.BeanNameAware;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//@Component
public class BeanLifeComponent implements BeanNameAware {
    
    

    @PostConstruct
    public void postConstruct() {
    
    
        System.out.println("执行 @PostConstruct");
    }

    public void init() {
    
    
        System.out.println("执行 init-method");
    }

    public void use() {
    
    
        System.out.println("使用 bean");
    }


    @PreDestroy
    public void preDestroy() {
    
    
        System.out.println("执行了 @PreDestroy");
    }

    public void setBeanName(String s) {
    
    
        System.out.println("执行了 Aware 通知");
    }
}

xml 配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:content="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <content:component-scan base-package="beans"></content:component-scan>
    <bean id="beanLifeComponent" class="beans.BeanLifeComponent" init-method="init"></bean>
</beans>

测试:

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    
    
    public static void main(String[] args) {
    
    
                ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        BeanLifeComponent beanLifeComponent = context.getBean("beanLifeComponent", BeanLifeComponent.class);
        beanLifeComponent.use();
        context.destroy();

    }
}

结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/chenbaifan/article/details/125694638