Bean 作用域和生命周期

article/2025/9/22 21:55:58

文章目录

    • 引入 Lombok
    • 1. Bean 的作用域问题
    • 2. 作用域定义
      • 2.1 作用域类型
      • 2.2 Bean 作用域的设置
    • 3. Bean 的生命周期
      • 3.1 执行流程:
      • 3.2 **代码示例:**
    • 4.Spring 生命周期

Spring 容器是用来存储和读取 Bean 的 , 因此 Bean 是 Spring 中最核心的操作资源.

引入 Lombok

编写代码过程中 , bean 对象如果有多个属性 , 创建 Getter , Setter, 构造方法 等方法 , 会产生大量冗长的代码. 那么为了使代码更加简洁 , 我们可以使用 Lombok 框架 , 只需要一行注释 , 就可以避免大量冗长的代码. 需要注意的是,Lombok并不是Java语言的一部分,而是一个第三方库,需要在项目中引入Lombok的jar包才能使用。同时,由于Lombok是通过注解来实现代码生成的,因此在使用Lombok时需要确保IDE和编译器支持注解处理。

Maven 中复制 lombok 依赖

image-20230416221144704

pom.xml 插入 lombok 依赖

image-20230416221529325

代码示例:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private String name;private int age;private String email;
}

在这个示例中,我们使用了Lombok的@Data、@NoArgsConstructor和@AllArgsConstructor注解。

@Data注解可以自动生成getter、setter、equals、hashCode和toString等方法,省去了手动编写这些方法的麻烦。

@NoArgsConstructor注解可以自动生成无参构造函数,方便我们在创建对象时使用。

@AllArgsConstructor注解可以自动生成全参构造函数,方便我们在创建对象时同时设置对象的属性值。


1. Bean 的作用域问题

假设有一个公共的 Bean , 提供给用户 A 和 用户 B 使用 , 如果 A 修改了 Bean 的公共数据 , 导致 B 在使用时发生与预期不符的错误.

创建一个实体类

@Setter
@Getter
@ToString
public class User {private int id;private String name;
}

存储 User 对象

@Component
public class UserBeans {@Beanpublic User user(){//伪代码User user = new User();user.setName("王五");user.setId(10);return user;}
}

UserController 修改 Bean 的公共数据:

@Controller
public class UserController {@Autowiredprivate User user;public void sayHi(){System.out.println(user);//修改 UserUser myUser = user;myUser.setName("张三");System.out.println("myUser->" + myUser.getName());}
}

UserController2 访问 Bean 中的公共数据:

@Controller
public class UserController2 {@Resourceprivate User user;public void sayHi2(){System.out.println("user ->" + user);}
}

启动类中调用:

public class App {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController userController = context.getBean("userController", UserController.class);userController.sayHi();UserController2 userController2 = context.getBean("userController2", UserController2.class);userController2.sayHi2();}
}

结果发现公共数据被修改:

image-20230416223136761

出现上述问题的原因是 , Bean 默认状态下是单例模式 , 即所有人使用的都是同一个对象 , 那么当多个用户并发执行时 , 一定会篡改公共数据.


2. 作用域定义

2.1 作用域类型

1. singleton(单例作用域)

  • 描述: 由于 Spring 框架除了追求高效还追求性能 , 因此使用单例模式作为默认作用域. 该作用域下的 Bean 在整个 IoC容器中只存在一份 , 无论是获取还是注入都是同一个对象.
  • 场景: 通常无状态的 Bean 使用该作用域. (无状态指对象的属性无需更新)

2. prototype(原型作用域)

  • 描述: 在该作用域下 , 每次 Bean 的请求都会创建新的实例.
  • 场景: 通常有状态的 Bean 使用该作用域.

3. request(请求作用域)

  • 描述: 每次 Http 请求都会创建一个 Bean 对象.
  • 场景: 一次 Http 请求和响应共享的 Bean 对象 , 适用于 Spring MVC 项目

4. session(会话作用域)

  • 描述: 一次 Http session 中 , 定义一个 Bean 对象 , 适用于 Spring MVC项目
  • 场景: 每次 Session 会话共享一个 Bean 对象.

5. application(全局作用域)

  • 描述: 一个 Http Servlet context 中共享一个 Bean
  • Web 应用的上下文 , Spring MVC.

**6. websocket(Http WebSocket 作用域) **

  • 描述: 在一个 Http WebSocket 生命周期中 , 定义一个 Bean对象.
  • 场景: 只适用于 Spring WebSocket 项目

总结:

  • 我们目前使用的是 Spring core 项目 , 因此只能使用 singleton 和 prototype 作用域.
  • singleton 作用于 IoC 容器 , application 作用域 Servlet 容器.

2.2 Bean 作用域的设置

我们可以在存储Bean对象的时候通过 @Scope 注解设置作用域.

代码示例:

我们可以通过两种方式设置作用域:

  • 直接使用 prototype 设置作用域

存储 Bean 时设置作用域:

@Component
public class UserBeans {@Bean@Scope("prototype")public User user(){//伪代码User user = new User();user.setName("王五");user.setId(10);return user;}
}

注入时会创建新对象:

@Controller
public class UserController {@Autowiredprivate User user;
}

再次运行, 结果与预期一致.

image-20230417085702030

  • 使用 @ Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

为什么使用这么麻烦的方式 , 因为有自动提示 , 可以防止单词拼错 以及 使用不合法的作用域.

@Component
public class UserBeans {@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public User user(){//伪代码User user = new User();user.setName("王五");user.setId(10);return user;}
}

3. Bean 的生命周期

所谓 Bean 的生命周期就是 Bean 从创建到销毁的过程.

3.1 执行流程:

大致流程分为以下五步:

1.实例化 Bean (为Bean 分配内存空间)

当 Spring 容器加载配置文件时 , 会根据其中注册的 Bean 利用反射机制 Bean 实例.

2.设置属性(Bean 对象的注入和装配)

Bean 实例化之后, Spring 容器会自动将配置文件中 , 定义的属性值注入到 Bean 实例中(包括基本类型, 对象, 集合).

3.Bean 初始化

  • 实现了各种 Aware 通知的方法 , 如 BeanNameAware , BeanFactoryAware , ApplicationContextAware等.
  • 初始化前置方法: Spring执行所有实现了BeanPostProcessor接口的类的postProcessBeforeInitialization()方法,这些类可以在bean初始化之前进行一些自定义的处理。
  • 执行初始化方法: (有两种) 1.注解方式: @PostConstruct , 2.xml 方式: init-method方法. 如果bean实现了InitializingBean接口,Spring将调用其afterPropertiesSet()方法
  • 初始化后置方法: Spring执行所有实现了BeanPostProcessor接口的类的postProcessAfterInitialization()方法,这些类可以在bean初始化之后进行一些自定义的处理

4.使用 Bean

5.销毁 Bean 对象

  • 常用方法: 如 @PreDestroy , DisposableBean 接口方法

image-20230418164754381

例如: 将买房子视为 Bean 的生命周期

  1. 买一个房子 , 相当于实例化 Bean 开辟内存空间
  2. 装修房子 , 相当于配置文件给 Bean 注入各种属性
  3. 买家具 , 相当于初始化 Bean
  4. 住房 , 相当于使用 Bean
  5. 将房子卖出 , 相当于销毁 Bean

由此也可以得出 , 配置文件给 Bean 注入属性 , 必须排在初始化之前 , 因为初始化可能调用属性.(装修完才能安家具)

3.2 代码示例:

PostConstruct 版

@Component
public class UserBeans implements BeanNameAware {@PostConstructpublic void PostConstruct(){System.out.println("执行了 PostConstruct");}@PreDestroypublic void PreDestroy(){System.out.println("执行了 PreDestroy");}@Overridepublic void setBeanName(String s) {System.out.println("执行了 setName" + s);}
}

xml 版

我们可以在配置文件中设置各种属性.

image-20230417095529414

@Component
public class BeanComponent implements BeanNameAware {@Overridepublic void setBeanName(String s) {System.out.println("执行了通知 BeanName ->" + s);}//xml 方式的初始化方法public void myInit(){System.out.println("XML 方式初始化");}public void sayHi(){System.out.println("执行 sayHi");}//xml 方式的销毁public void preDestroy(){System.out.println("执行了销毁方法");}
}

调用启动类:

public class App {public static void main(String[] args) {ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");BeanComponent component = context.getBean("beanComponent" , BeanComponent.class);component.sayHi();context.destroy();}
}

结果与预期一致:

image-20230417100941727

4.Spring 生命周期

Spring执行流程如下:

  1. 应用程序启动时,Spring容器被创建。
  2. Spring容器读取并解析配置文件,将其中的bean定义加载到内存中。
  3. Spring容器使用Java反射机制创建bean实例。
  4. Spring容器将配置文件中指定的属性值或引用注入到bean实例中。
  5. 如果bean实现了InitializingBean接口,Spring容器将调用其afterPropertiesSet()方法。如果在配置文件中指定了init-method属性,则Spring容器将调用该方法。
  6. 如果bean实现了BeanPostProcessor接口,Spring容器将执行其postProcessBeforeInitialization()方法,进行一些自定义的处理。
  7. Spring容器将bean实例化后,将其放入容器中管理。
  8. 应用程序向Spring容器请求一个bean,Spring容器根据请求的名称或类型,从容器中返回一个bean实例。
  9. 如果bean实现了BeanPostProcessor接口,Spring容器将执行其postProcessAfterInitialization()方法,进行一些自定义的处理。
  10. 应用程序使用bean实例完成相应的业务逻辑。
  11. 应用程序关闭时,Spring容器将销毁所有的bean实例。
  12. 如果bean实现了DisposableBean接口,Spring容器将调用其destroy()方法。如果在配置文件中指定了destroy-method属性,则Spring容器将调用该方法。

需要注意的是,Spring执行流程中的每一步都可以进行自定义的配置和处理,以满足不同的业务需求
用程序向Spring容器请求一个bean,Spring容器根据请求的名称或类型,从容器中返回一个bean实例。


http://chatgpt.dhexx.cn/article/6W2fb6K3.shtml

相关文章

【Spring6】| Bean的作用域

目录 一:Bean的作用域 1. singleton(单例) 2. prototype(多例) 3. 其它scope 4. 自定义scop(了解) tips:首先给大家推荐两款好用的免费软件:动图抓取软件&#xff1…

Spring Bean的作用域及生命周期

目录 前言: Bean的作用域(Scope) 单例模式 原型模式(多例作用域) 请求作用域(request) 会话作用域 全局作用域 网络长连接 Spring执行流程 Bean的生命周期 测试 小结: 前…

Spring中Bean的作用域问题

文章目录 一、通过案例来简单体会一下Bean的作用域问题二、作用域定义三、Bean的作用域分类singletonprototyperequestsessionapplication(了解)singleton(单例作用域) 和 application (全局作用域)的区别w…

面试:Bean 作用域是什么?它有几种类型?

Spring 框架作为一个管理 Bean 的 IoC 容器,那么 Bean 自然是 Spring 中的重要资源了,那 Bean 的作用域是什么意思?又有几种类型呢?接下来我们一起来看。 PS:Java 中的公共类可称之为 Bean 或 Java Bean。 1、作用域…

Spring系列8:bean的作用域

本文内容 bean定义信息的意义介绍6种bean的作用域 bean定义信息的意义 Spring中区分下类、类定义信息,类实例对象的概念?不容易理解,以餐馆中点炒饭为例。 类: 相当于你看到菜单上炒饭这个菜品,有这个菜。 类定义…

【Java第34期】:Bean的六种作用域

作者:有只小猪飞走啦 博客地址:https://blog.csdn.net/m0_62262008?typeblog 内容:介绍Bean的六种作用域的效果以及适用场景 文章目录 前言一,作用域定义以及Bean的六种作用域是什么?二,singleton&#…

Bean的六种作用域

什么是Bean的作用域呢? 这里的"作用域"还和Java中的类级,方法级等这几个作用域概念不太一样,我自己的一个理解是:在Spring执行的这段时间中,Bean的作用域就只有单例和多例这两种,在执行respect这段时间之中,就有它自己的作用域,而到了session中又有它自己的作用域,…

Spring Bean的作用域

在Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者。 目前Spring Bean的作用域或者说范围主要有五种。 作用域描述singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值…

设计原则-依赖倒置

名称:依赖倒置原则(Dependence Inversion Principle) 定义:程序要依赖于抽象接口,不要依赖于具体实现。依赖倒置原则的中心思想是面向接口编程。 高层模块不应依赖于低层模块,二者都应该依赖于抽象抽象不…

C++之依赖倒置原则

C之依赖倒置原则 1. 依赖于具体抽象(接口),不依赖于具体的实现,也就是针对接口编程。 2. 实现高层业务和实现层、实现层和实现层之间的解耦合; 实例: 电脑框架和电脑内部的配件产品(或提供配…

【软件架构设计原则】开闭原则和依赖倒置原则

文章目录 软件架构设计原则开闭原则依赖倒置原则最后 软件架构设计原则 本文通过实例来讲解 开闭原则依赖导致原则 开闭原则 开闭原则(Open-Close Principle,OCP)是指一个软件实体(如类、模块和函数)应该对扩展开放…

java 依赖倒置_Java设计原则—依赖倒置原则(转)

依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义: 高层模块不应该依赖底层模块,两者都应该依赖其抽象; 抽象不应该依赖细节; 细节应该依赖抽象。 依赖倒置原则在Java语言中的表现是: 模块间的…

设计模式 依赖倒置原则

文章目录 依赖倒置原则依赖倒置原则实战使用依赖倒置原则进行改变案例中两种方式的类图 依赖倒置原则 定义: 程序要依赖于抽象接口, 不要依赖于具体实现, 对抽象进行编程, 而不是对实现进行编程, 降低了客户与实现模块的耦合. 高层模块不应该依赖底层模块, 都应该依赖抽象(接口…

依赖倒置原则应用-司机开车案例

依赖倒置原则 依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。 依赖倒置原则三层含义: …

4.3 依赖倒置原则

一. 依赖倒置原则的定义 1.核心思想 依赖倒置原则(Dependence Inversion Principle,DIP) *1.高层模块 不应该依赖 低层模块, 二者都应该依赖其抽象; *2.抽象 不应该依赖 细节, 细节 应该依赖 抽象; 接口和抽象类价值在于设计; *3.依赖倒…

C# 依赖倒置原则(DIP)

目录 一,引子 1.1 传统的程序架构 1.2 依赖倒置 1.3 依赖倒置的作用 二,依赖注入 一,引子 1.1 传统的程序架构 在程序执行过程中,传统的程序架构如图: 可以看到,在传统的三层架构中,层与…

设计模式-依赖倒置原则

依赖倒置原则 1 依赖倒置原则的定义 依赖倒置原则(Dependence Inversion Principle,DIP)这个名字看着有点别扭,“依赖”还“倒置”,这到底是什么意思?依赖倒置原则的原始定义是: High level modules shou…

C++ 依赖倒置原则

.依赖倒置原则 定义 高层模块不应该依赖于底层模块,而应该依赖于抽象。抽象不应依赖于细节,细节应依赖于抽象。解决的问题 类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般…

【设计原则】依赖倒置原则 (面向接口编程)

本文将以Java为基础,讲解开发中,面向接口编程的知识,只要以简单的例子为主,讲解如何进行面向接口编程,并会区分其于面向实现编程的区别。下面先讲一讲依赖倒置原则,再过渡到案例解释。 本文目的在于用极其简单的图解帮…

设计模式 — 6大设计原则(依赖倒置和接口隔离原则)

设计模式 依赖倒置原则示例 一示例 二依赖的三种写法总结 接口隔离原则实例 一总结 依赖倒置原则 依赖倒置原则(Dependence Inversion Principle,DIP)这个名字看着有点别扭,“依赖” 还 “倒置” ,这到底是什么意思&a…