Spring 的执行流程以及 Bean 的作用域和生命周期

文章目录

Bean 的作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作用域。Bean 有6种作用域

  1. singleton:单例作用域
  2. prototype:原型作用域(多例作用域)
  3. request:请求作用域 (一个http 请求共享一个 Bean)
  4. session:会话作用域 (一个会话共享一个 Bean)
  5. application:全局作用域 (一个上下文对象共享一个 Bean)
  6. websocket:HTTP WebSocket 作用域(只适用于Spring WebSocket 项目)

其中后面四种是基于 Spring MVC 生效的

更改作用域的方式

使用 @Scope 标签就可以用来声明 Bean 的作用域,不指定默认为 singleton(单例)作用域

方式一:直接设置值

java 复制代码
@Scope("prototype")
public class User {
    private Integer id = 1;
    private String name = "张三";

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

方式二:使用枚举设置

java 复制代码
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class User {
    private Integer id = 1;
    private String name = "张三";

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

singleton

作用域下的Bean在IoC容器中只存在⼀个实例:获取Bean及装配Bean都是同⼀个对象。

通常Bean对象的属性状态不需要更新的时候使用。

该种作用域也是 Bean 默认的作用域

java 复制代码
@Getter // Lombok
@Setter // Lombok
@Controller
public class User {
    private Integer id = 1;
    private String name = "张三";

    @Override
    public String toString() {
        return "UserController{" +
                "id = " + id + ", " +
                "name = " + name +
                '}';
    }
}
java 复制代码
public class Start {
    public static void main(String[] args) {
        // 获取 Spring 的上下文对象
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取 Bean 对象
        User user1 = context.getBean("user", User.class);
        System.out.println("user1 -> " + user1);
        user1.setId(2);
        user1.setName("王五");

        User user2 = context.getBean("user", User.class);
        System.out.println("user2 -> " + user2);
    }
}

可以看到,当第一次获取 bean 时在没有任何修改的情况下,打印的结果是初始值。但对这个 bean 进行修改后,之后再次获取该 bean 的结果就是修改后的结果

prototype

每次对该作用域下的Bean的请求都会创建新的实例,也就是说获取Bean 和装配Bean(即通过@Autowired注入)都是新的对象实例。

java 复制代码
@Getter // Lombok
@Setter // Lombok
@Controller
@Scope("prototype")
public class User {
    private Integer id = 1;
    private String name = "张三";

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

此时,user1 和 user2就互不干扰了

Spring 执行流程

分为四个步骤

  1. 启动 Spring 容器(加载配置文件)
  2. 实例化 Bean(分配内存空间)
  3. Bean 注册到 Spring 中(存)
  4. 将 Bean 装配到需要的类(取)

Bean 的生命周期

Bean 的生命周期分为五大部分

  1. 实例化 Bean(分配内存空间)
  2. 设置属性(注入和装配)
  3. Bean 初始化
    1. 实现各种 Aware 通知的方法(例如 BeanNameAware,BeanFactoryAware的接口方法)
    2. 执行 BeanPostProcessor 初始化前置方法
    3. 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行
    4. 如果有指定init-method 方法则执行
    5. 执行 BeanPostProcessor 初始化后置方法
  4. 使用 Bean
  5. 销毁 Bean(销毁方法例如:如 @PreDestroy、DisposableBean 接口方法、destroy-method)
java 复制代码
@Controller
public class User {
    private Integer id = 1;
    private String name = "张三";

    // 初始化
    @PostConstruct
    public void PostConstruct(){
        System.out.println("执行初始化");
    }

    // 指定 init-method 方法
    public void Init(){
        System.out.println("执行Init-method方法");
    }

    // 销毁
    @PreDestroy
    public void Des(){
        System.out.println("执行销毁");
    }

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

如果需要使用指定的 init-method 方法,需要在xml 文件进行配置

xml 复制代码
<bean id="user" class="spring.demo.entity.User" init-method="Init"></bean>

接着启动类启动

java 复制代码
public class Start {
    public static void main(String[] args) {
        // 获取 Spring 的上下文对象
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取 Bean 对象
        User user1 = context.getBean("user", User.class);
        System.out.println(user1);
        user1.Des();
    }
}
相关推荐
萧瑟余晖5 分钟前
Java深入解析篇三十之Java日志框架
java·开发语言
Suxing91 小时前
C语言基础分享:从“租房”到“拆迁”,C语言动态内存管理
java·数据结构·算法
减瓦1 小时前
别让 Java 版本选择变成生产事故
java
ikun_文1 小时前
Java集合框架Collection拓展进阶
java·java ee
码匠许师傅1 小时前
【C++ 面试真题】聊聊 C++ 的虚函数和多态
java·c++·面试
chuan.bai2 小时前
Java RAG 实战(第 8 篇):Spring Boot RAG 查询 API
java·spring boot·贪心算法
FlyWIHTSKY2 小时前
idea中集成claude功能
java·ide·人工智能·intellij-idea·cloudera
Java成神之路-2 小时前
Spring AI 统一结构化返回:ChatModel / ChatClient 两种实现方式
java·springaialibaba
Mr. zhihao2 小时前
深度解析:为什么Java序列化需要搭配ByteArrayOutputStream?IO装饰器模式的精妙设计
java·开发语言·装饰器模式
存在morning2 小时前
【Python 开发实践 一】Python vs Go vs Java 三门语言对比
java·python·golang