0


【Spring 】执行流程解析:了解Bean的作用域及生命周期

** 哈喽,哈喽,大家好~ 我是你们的老朋友:****保护小周ღ **


今天给大家带来的是** Spring 项目的执行流程解析 Bean 对象的6 种作用域以及生命周期本文将为大家讲解,一起来看看叭~**


本期收录于博主的专栏:JavaEE_保护小周ღ的博客-CSDN博客

适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaEE基础知识”。

更多精彩敬请期待:保护小周ღ ★,°:.☆( ̄▽ ̄)/$:.°★


一、Spring 的执行流程

Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 读取Spring 配置文件-> 实例化 Bean 对象(分配内存空间,从无到有) -> Bean 注册到 Spring 中(存操作) -> 将 Bean 注入到需要的类中(取操作)。

** Spring 注解相关操作感兴趣得老铁可以阅读博主得另一篇博客:**

【Spring】使用注解读取和存储Bean对象_保护小周ღ的博客-CSDN博客


二、Bean 的作用域

Spring 是一个包含众多工具方法的 IoC 容器,也可以认为 Spring 就是用来读取和存储 Bean 对象的,那么 Bean 对象就是Spring 中一个至关重要的角色。

2.1 通过案例来观察 Spring 中Bean 对象默认的作用域

设计一个公共的 Student 类的Bean对象,注册到 Spring 容器中,提供给 A用户 和 B用户使用,A用户不讲武德悄悄地修改 Student 类型Bean 对象的数据,B 用户在使用的 Bean 对象的时候发现数据已经被篡改了,相当的恼火~

Student 类如下:

@Data
public class Student {
    private int id; // 学号
    private String name; // 姓名
    private int age; // 年龄
    private String sex; // 性别
}

给大家介绍一个第三方的框架: **lombok **

"Lombok"是一个在Java开发中非常常用的开源框架。它的目标是通过自动化生成Java代码的样板代码来简化开发过程,减少冗余代码,并提高代码的可读性。

Lombok提供了一系列的注解,例如@Data、@Getter、@Setter、@NoArgsConstructor等,通过在类上添加这些注解,可以自动生成对应的getter、setter、构造函数等方法。这样可以大大简化Java类的编写,提高开发效率

要使用Lombok,您需要在项目中添加Lombok的依赖,并在IDE中安装Lombok插件,以便在编译和运行时正确处理Lombok的注解。

** Lombox 依赖:**

 <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
      <scope>provided</scope>
 </dependency>

博主使用 @Data 注解就来源于 lombox ,@Data包含了 @Setter 和 @Getter 注解,也就是自动给Student 类创建 set 和 get 方法。


公共的Bean 对象,注入到 Spring 中:

@Component
public class Users {
    //使用方法注解将 学生张三注入到 Spring 中
    @Bean
    public Student getStu() {
        Student stu = new Student();
        stu.setId(123456);
        stu.setName("张三");
        stu.setAge(18);
        stu.setSex("男");
        return stu;
    }
}

用户A 从Spring 中获取 stu Bean 对象,并进行修改:

@Controller
public class UserA {

    //从Spring 中获取Bean 对象
    @Resource //@Autowired
    private Student stu;

    public Student getStu() {
        System.out.println("用户A获取原Bean对象"+ '\n'+ stu.toString());

        //对原Bean 的数据进行修改
        stu.setName("李四");
        stu.setSex("女");
        return stu;
    }
}

用户B 从Spring 中获取 stu Bean 对象,进行使用(打印):

@Controller
public class UserB {
    //从Spring 中获取Bean 对象
    @Resource //@Autowired
    private Student stu;

    public Student getStu() {
        System.out.println("用户B获取被用户A修改过的 Bean :" + '\n' + stu.toString());
        return stu;
    }
}

启动类中获取用户A 和 用户B 对象,调用getStu() 方法,获取 stu Bean对象:

public class App {
    public static void main(String[] args) {
        //1. 获取Spring 上下文对象,里面管理着 bean 对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        //2. 获取指定的 Bean 对象
        UserA userA = context.getBean("userA", UserA.class);
        Student stuA = userA.getStu();
        System.out.println("用户A:" + stuA.toString());

        UserB userB = context.getBean("userB", UserB.class);
        Student stuB = userB.getStu();
        System.out.println("用户B:"+ stuB.toString());
    }
}


案例分析:

以上操作的的执行结果的原因 Bean 默认情况下是单例模式(singleton:一个类只有一个实例),也就是说用户 A 和 用户 B 操作的都是同一个Bean 对象,所以用户A 修改了唯一的 Bean , 用户B 获取到的 Bean 就是被修改后的了。

在Spring 中Bean 的作用域默认是singleton 单例模式~


2.2 Bean 对象的 6 种作用域

限定程序中变量的可用范围叫做作用域,Bean 对象的作用域指的是在 Spring 整个生命周期中的某种行为模式,例如:单例作用域(singleton)的意思就是在整个 Spring 中只有一份实例,且全局共享,当其他人修改了这个唯一的一份实例后,另一个人再获取到的实例,就是被修改后的值了。

Spring框架为Bean对象提供了多种作用域选项。以下是Spring框架中常见的6种Bean对象作用域:

  1. Singleton(单例):在整个应用程序中只存在一个Bean实例。每次请求都将返回相同的实例。获取Bean(通过applicationContext.getBean等方法获取),装配Bean(即通过@Autowired,@Resource)
  2. Prototype(原型):每次请求将创建一个新的Bean实例。每次请求都返回不同的实例。获取Bean(通过applicationContext.getBean等方法获取),装配Bean(即通过@Autowired,@Resource)
  3. Request(请求):在每次HTTP请求中,都会创建一个新的Bean实例。每个HTTP请求都返回不同的实例。
  4. Session(会话):在用户会话期间,都会创建一个新的Bean实例。对于同一用户的后续请求,将返回相同的实例。
  5. Global Session全局会话):在整个应用程序的全局会话期间,都会创建一个新的Bean实例。对于同一全局会话的后续请求,将返回相同的实例。
  6. Application应用程序):在整个Web应用程序的生命周期内,都会创建一个新的Bean实例。每个Web应用程序都返回相同的实例。

注意:作用域的使用取决于具体的应用场景和需求。Spring框架还支持自定义作用域,普通的Spring 项目只支持 singleton (单例),prototype (多例),剩下的4种状态是在 SpringMVC项目中可以设置。


2.3 给Bean 对象设置作用域

使用 @Scope 标签就可以用来声明 Bean 的作用域, 如以下代码所示:

@Component
public class Users {
    //声明为多例模式,每此请求都会实例一个新的Bean对象
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 
    //使用方法注解将 学生张三注入到 Spring 中
    @Bean(name = "stu") //给 Bean 对象起一个名字
    public Student getStu() {
        Student stu = new Student();
        stu.setId(123456);
        stu.setName("张三");
        stu.setAge(18);
        stu.setSex("男");
        return stu;
    }
}

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

1. 直接设置值: @Scope("prototype")

英语好的朋友可以直接把单词记住,也是蛮直接方便的昂~

2.使用枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

此时我们给上述例子Student 类型的Bean 对象设置了多例模式,这个时候运行一下看看:


三、Bean 的生命周期

所谓 Bean(对象) 生命周期指的就是一个普通对象从创建到销毁的整个过程,我们把整个过程称之为对象的生命周期。Bean 对象也就是一个普通的实例对象。

Bean 对象的生命周期分为以下五个部分:

1. 实例化: Bean (为 Bean对象分配内存空间(堆区))

2. 设置属性:在实例化后,容器会将配置的属性值或引用注入到Bean对象中,可以通过构造函数、setter方法或字段注入的方式完成。

3. Bean 对象的初始化 :Bean对象的初始化流程通常包括以下几个步骤:

  • 加载Bean定义:首先,容器会读取配置文件或通过注解扫描等方式,获取Bean的定义信息,并将其存储在内存中。
  • 创建Bean实例:根据Bean的定义信息,容器会实例化Bean对象。这可以通过调用默认构造函数或使用工厂方法等方式来完成。
  • 注入依赖:一旦Bean实例创建完成,容器会检查Bean定义中所声明的依赖关系,并将相应的依赖注入到Bean实例中。依赖注入可以通过构造函数、setter方法或字段注入方式来实现。
  • 实现Aware接口:如果Bean实现了特定的Aware接口(如BeanNameAware、BeanFactoryAware等),容器会调用相应的回调方法,以便提供一些额外的关于容器的信息。
  • Bean初始化前的处理:在Bean实例初始化之前,容器会调用BeanPostProcessor接口的实现类的postProcessBeforeInitialization()方法,允许开发者在初始化之前进行一些自定义逻辑的处理。
  • 初始化Bean:实例化过程完成后,容器会根据Bean定义中的配置调用初始化方法。这可以是通过在Bean类中标注@PostConstruct注解或实现InitializingBean接口的afterPropertiesSet()方法来完成。
  • Bean初始化后的处理:在Bean实例初始化之后,容器会再次调用BeanPostProcessor接口的实现类的postProcessAfterInitialization()方法,允许开发者在初始化之后进行一些自定义逻辑的处理。
  • 完成Bean的初始化:至此,Bean的初始化流程完成,可以将其添加到容器中,供其他Bean进行依赖注入或其他操作使用。

需要注意的是,以上是一个典型的Bean对象初始化流程,实际应用中可能会有一些差异,例如,使用了代理、继承了父类等情况会导致初始化流程有所不同。同时,不同的容器实现也可能会有一些细微的差异。

4. 使用 Bean : Bean 对象,就是一个普通的实例化的对象,使用都是一样的,看咋类是咋设计的

5. 销毁 Bean :

销毁Bean对象可以通过以下几种方式实现:

  • 使用Destroy方法:可以在Bean类中实现DisposableBean接口,并重写其中的destroy()方法。在容器销毁Bean实例时,会自动调用该方法来销毁Bean对象。
public class MyBean implements DisposableBean {
    // ...
    
    @Override
    public void destroy() {
        // 销毁操作
        // ...
    }
}
  • 使用@PreDestroy注解:可以在Bean类的销毁方法上添加@PreDestroy注解,当容器销毁Bean实例时,会触发该注解标记的方法进行销毁操作。
public class MyBean {

    // ...
    
    @PreDestroy
    public void destroy() {
        // 销毁操作
        // ...
    }
}
  • 配置销毁方法:在配置文件(如XML配置文件)中,可以通过指定destroy-method属性来指定Bean销毁时调用的方法。该方法可以是Bean类中的任意公开方法。
<bean id="myBean" class="com.example.MyBean" destroy-method="destroy">
    <!-- ... -->
</bean>

需要注意的是,销毁Bean对象的时机由容器管理,通常发生在容器关闭时。如果是单例Bean,则在容器关闭或手动调用销毁方法时触发销毁操作。如果是原型或会话作用域(session scoped)的Bean,则需要手动销毁。


执行流程图 :


实例化和初始化的区别:

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

总的来说,实例化是创建Bean实例的过程,而初始化是对已创建的Bean对象进行属性赋值和回调方法调用的过程。实例化是创建Bean对象的基础步骤,而初始化则是在实例创建完成后对Bean对象进行配置和准备的重要步骤。


举个例子形象的描述Bean 对象的生命周期:买房子

  1. 实例化:在某市盖了一套毛坯房(从无到有,将字节码转换为内存中的对象,只是分配了内存)

  2. 设置属性:(容器会将配置的属性值或引用注入到Bean对象中)。购买装修材料,引入外部资源。

  3. 初始化:给房子装修~

  • 各种通知:联系各种装修师傅,水工,电工,瓦工……
  • 初始化的前置工作:装修师傅现场考察毛坯房的环境,制定装修方案。
  • 进行初始化工作:(@PostConstruct 初始化、init-method 初始化),装修师傅进行装修,其中有装修技术好的,有装修技术差的。
  • 初始化的后置工作: 装修完毕之后对房子进行清理。
  1. 使用 Bean 对象: 拎包入住~

  2. 销毁 Bean 对象: 拆,或者卖掉这套房子~

一定是先设置属性,再进行初始化操作。


好了,到这里,**【Spring 】执行流程解析:了解Bean的作用域及生命周期 **博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。

下期预告:【SpringBoot】框架~

感谢每一位观看本篇文章的朋友,*更多精彩敬请期待:保护小周ღ ★,°:.☆( ̄▽ ̄)/$:.°★* **

遇见你,所有的星星都落在我的头上……

标签: spring java 后端

本文转载自: https://blog.csdn.net/weixin_67603503/article/details/131284480
版权归原作者 保护小周ღ 所有, 如有侵权,请联系我们删除。

“【Spring 】执行流程解析:了解Bean的作用域及生命周期”的评论:

还没有评论