Spring生命周期(一)

本文详细介绍了Spring框架中Bean的生命周期管理,重点讲解了@PostConstruct和@PreDestroy注解的使用,以及如何通过Spring容器进行Bean的初始化和销毁。此外,还讨论了Spring容器和相关接口的角色以及如何在第三方组件中应用生命周期管理方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 生命周期管理概述

        Spring生命周期,完整的名称是Spring Bean生命周期管理,也就是Spring中Bean的创建、使用以及销毁的过程。

        关于Spring Bean的生命周期,需要达成2项学习结果:

  • 能够完整介绍Spring Bean的生命周期
  • 能够通过生命周期管理方法,参与到Bean的生命周期中

        为了更好的理解Spring Bean的生命周期,可以先了解生命周期管理方法的概念,再逐步掌握Spring Bean生命周期各阶段的细节。

2. 生命周期管理方法

2.1 生命周期管理方法概述

        生命周期管理方法是对象在创建、初始化和销毁时自动调用的一些方法。这些方法提供了对象在不同生命周期阶段的行为。

        生命周期管理方法的设计在第三方组件或框架中非常常见,主要是为了保证框架的可扩展性。

        生命周期管理方法在一些场景中也被称为“生命周期回调方法”,“生命周期钩子函数”等。

        理解生命周期管理方法的切入点在于“自动调用”,也就是说不是由开发者手动调用。

        我们先来看一下由开发者手动调用的生命周期管理方法的示例。如下图所示,开发者在编码的过程中决定了何时创建对象、何时使用对象、何时销毁对象,这些方法的调用时机由开发者决定。

        但是当使用第三方组件和框架时,组件或框架中的对象的生命周期一般不是由开发者控制,而是按照特定的流程执行。

        例如Java EE中经典的HttpServlet组件,其生命周期完全由Web服务器控制。此时,如果开发者想要参与到这些组件的生命周期中,在特定的节点执行某段自定义的逻辑,就需要依靠生命周期管理方法。

        简单来说,组件的设计者约定了在特定的节点会调用特定的方法,开发者可以通过重写该方法,达到在特定节点执行自定义逻辑的目的。

2.2 @PostConstruct

        Spring 为了方便用户扩展功能,提供了Bean的生命周期管理方法。为了简化方法的声明,Spring提供了通过注解声明生命周期管理方法的功能。

        @PostConstruct注解用于将Spring Bean中的方法声明为生命周期管理方法,该方法将在Bean实例化后被调用。

  • 被标记的方法要求无参且返回值类型为void
  • 不能标记静态方法
  • 对应的方法一般用于初始化:如初始化缓存,初始化数据库连接等
  • 在依赖注入之后被调用
  • 对于Scope为Singleton的Bean,默认在容器创建完成后进行实例化和初始化
  • 对于Scope为Prototype的Bean,在第一次获取该实例时进行实例化和初始化

        新建一个Maven项目,在项目中引入Spring框架的依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.11</version>
</dependency>
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

        新建com.obj.spring.TagService类,并声明初始化方法:

package com.obj.spring;

import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@Component
// @Scope("prototype")
public class TagService {
    /**
     * 本地缓存,缓存了标签信息,线程安全集合,扩展下知识广度
     */
    private final CopyOnWriteArrayList<String> tags = new CopyOnWriteArrayList<>();
    @PostConstruct // 创建对象以后调用
    public void initTags(){
        tags.add("应季");
        tags.add("爆款");
        tags.add("进口");
        tags.add("生鲜");
        tags.add("健身");
        System.out.println("初始化标签:"+tags.toString());
    }
    public List<String> getTags() {
        return tags;
    }
}

        新建com.obj.spring.ContextConfig类,声明Spring框架配置:

package com.obj.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.obj.spring")
public class ContextConfig {
}

        新建com.obj.spring.Test类,编写测试内容:

package com.obj.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);
        // 测试@PostConstruct执行时机:创建容器 - > 实例化Bean -> 初始化
        System.out.println("第一次获取Bean");
        TagService service = context.getBean(TagService.class);
        service.getTags().forEach(System.out::println);
    }
}

        在com.obj.spring.TagService的类前添加@Scope("prototype")注解,重新运行案例,观察@PostConstruct标记的方法执行时机的变化。

2.3 @PreDestroy

        @PreDestroy注解用于将Spring Bean中的方法声明为生命周期管理方法,该方法将在Bean实例被销毁前执行。

  • 被标记的方法要求无参且返回值类型为void
  • 不能标记静态方法
  • 对应的方法一般用于清空缓存,释放资源等
  • 关闭容器时候会自动调用
  • 只有Scope为Singleton的Bean,销毁时候才会执行销毁方法
  • 这个方法不是绝对可靠,依赖JVM的关闭流程,直接终止进程时不会执行

在com.obj.spring.TagService中添加销毁方法:

package com.obj.spring;

import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@Component
public class TagService {
    private final CopyOnWriteArrayList<String> tags = new CopyOnWriteArrayList<>();
    @PostConstruct // 创建对象以后调用
    public void initTags(){
        tags.add("应季");
        tags.add("爆款");
        tags.add("进口");
        tags.add("生鲜");
        tags.add("健身");
        System.out.println("初始化标签:"+tags.toString());
    }
    public List<String> getTags() {
        return tags;
    }
    @PreDestroy
    public void destroy(){
        System.out.println("清除标签缓存:"+tags.toString());
        tags.clear();
    }
} 

        在com.obj.spring.Test中添加关闭Spring Context的操作:

package com.obj.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);
        // 测试@PostConstruct执行时机:创建容器 - > 实例化Bean -> 初始化
        System.out.println("第一次获取Bean");
        TagService service = context.getBean(TagService.class);
        service.getTags().forEach(System.out::println);
        // 关闭容器 -> 销毁Bean -> 调用@PreDestroy标记的方法
        ((AnnotationConfigApplicationContext)context).close();
    }
}

        在com.obj.spring.TagService的类前添加@Scope("prototype")注解,重新运行Test类,查看销毁方法是否还会被调用。

2.4 @PreDestroy原理

        @PreDestroy的执行原理:

        1、Spring在Java虚拟机上挂了关闭钩子,虚拟机关闭时候会自动执行钩子, Spring的钩子会关闭Spring容器。

        2、关闭容器的过程中,会销毁所有单例的Bean

        3、销毁单例的Bean时,会调用添加了@PreDestroy 注解的方法。

        如下图所示:

        这一过程,也就是Bean生命周期中的销毁阶段。如下图所示:

2.5 JVM Hook示例

package com.obj.spring;

import java.util.concurrent.TimeUnit;
public class ShutdownHookDemo {
    public static void main(String[] args) throws Exception{
        /*
         * runtime 代表正在运行的虚拟机
         * 可以通过runtime获取当前虚拟机的参数
         */
        Runtime runtime = Runtime.getRuntime();
        //获取当前JVM总内存数量
        long bytes = runtime.totalMemory();
        System.out.println("totalMemory:" + bytes/1024/1024+"MB");
        //给系统挂关闭钩子
        runtime.addShutdownHook( new Thread( () -> System.out.println("执行钩子了!") ) );
        System.out.println("挂完了");
        TimeUnit.SECONDS.sleep(2);
    }
}

2.6 initMethod和destroyMethod

        当程序中使用的Bean是第三方提供的类时,无法直接在源码中添加@PostConstruct或@PreDestroy注解,此时可以通过@Bean注解的属性实现。

        Spring为 @Bean注解提供了initMethod属性和destroyMethod属性,用于设置Bean的初始化方法和销毁方法,语法如下:

// init和destroy为Bean中两个方法的名称
@Bean(initMethod="init", destroyMethod="destroy")

        声明com.obj.spring.ThirdPartyClass,模拟一个第三方类:

package com.obj.spring;
import jakarta.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ThirdPartyClass {
    // 本地缓存,缓存了名字信息
    private CopyOnWriteArrayList<String> names = new CopyOnWriteArrayList<>();
    @PostConstruct
    public void init(){
        names.add("Tom");
        names.add("Jerry");
        names.add("Andy");
        System.out.println("初始化ThirdPartyClass缓存:"+names);
    }
    public List<String> getNames() {
        return names;
    }
    public void destroy(){
        names.clear();
        System.out.println("销毁ThirdPartyClass缓存:"+names);
    }
}

        声明Test2类,编写测试逻辑:

package com.obj.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);
        ThirdPartyClass thirdParty = context.getBean(ThirdPartyClass.class);
        // 关闭容器 -> 销毁Bean -> 调用@PreDestroy标记的方法
        ((AnnotationConfigApplicationContext)context).close();
    }
}

3. 认识Spring容器

3.1 Spring容器概述

        Spring容器是一种IoC容器,它负责创建、配置和管理Bean。Spring容器的基本特点和作用:

        1、容器负责实例化和管理bean,通过使用Spring容器,可以将对象的创建和配置解耦合。

        2、容器提供了一种可插拔的机制,使得应用程序能够更加灵活地配置和管理bean。

        3、容器支持IoC和AOP,通过使用IoC容器,开发者可以更加方便地实现依赖注入和AOP编程。

        BeanFactory和ApplicationContext是Spring容器的两个主要实现。

3.2 BeanFactory

        BeanFactory接口是Spring容器的基本接口,提供了Bean的管理方法。BeanFactory既是生产Bean的工厂,同时也是管理Bean的仓库。

        在BeanFactory中,所有的bean都是懒加载的,只有在使用时才会创建,因此它的启动速度比其他容器要快。

        Spring在BeanFactory的基础上扩展了ApplicationContext。根据Spring官方文档,除了极少数情况下,开发者应该使用ApplicationContext而非BeanFactory。

3.3 BeanFactory和FactoryBean的关系

        BeanFactory 和 FactoryBean 都是 Spring 框架中用于管理和创建对象的重要接口,但它们的作用和用途有所不同:

        1、BeanFactory是Bean的工厂,而FactoryBean是工厂类型的Bean。

        2、BeanFactory是IOC容器的核心接口,职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

        3、FactoryBean 是一个特殊的工厂接口,用于创建和管理特定类型的对象,允许定制化对象创建逻辑,并将工厂本身作为 Bean 进行管理。

        4、在实际使用中,BeanFactory 是 Spring 的核心容器接口,而 FactoryBean 则是一种用于创建定制化对象的机制。

3.4 ApplicationContext

        ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能。

        在前面的示例中我们实际使用的Spring容器是AnnotationConfigApplicationContext对象,它间接实现了ApplicationContext接口,简化的继承和实现关系如下图所示:

4. 总结

        1、生命周期管理方法是对象在创建、初始化和销毁时自动调用的一些方法

  • 这些方法提供了对象在不同生命周期阶段的行为
  • 生命周期管理方法的设计在第三方组件或框架中非常常见,主要是为了保证框架的可扩展性

        2、@PostConstruct注解用于将Spring Bean中的方法声明为生命周期管理方法,该方法将在Bean实例化后被调用

  • 被标记的方法要求无参且返回值类型为void
  • 不能标记静态方法
  • 对应的方法一般用于初始化:如初始化缓存,初始化数据库连接等
  • 在依赖注入之后被调用

        3、@PreDestroy注解用于将Spring Bean中的方法声明为生命周期管理方法,该方法将在Bean实例被销毁前执行

  • 被标记的方法要求无参且返回值类型为void
  • 不能标记静态方法
  • 对应的方法一般用于清空缓存,释放资源等
  • 关闭容器时候会自动调用
  • 只有Scope为Singleton的Bean,销毁时候才会执行销毁方法

        4、@PreDestroy的执行原理

  • Spring在Java虚拟机上挂了关闭钩子,虚拟机关闭时候会自动执行钩子, Spring的钩子会关闭Spring容器
  • 关闭容器的过程中,会销毁所有单例的Bean
  • 销毁单例的Bean时,会调用添加了@PreDestroy 注解的方法

        5、Spring为 @Bean注解提供了initMethod属性和destroyMethod属性,用于设置Bean的初始化方法和销毁方法。

        6、Spring容器是一种IoC容器,它负责创建、配置和管理Bean,BeanFactory和ApplicationContext是Spring容器的两个主要实现。

  • BeanFactory接口是Spring容器的基本接口,提供了Bean的管理方法。BeanFactory既是生产Bean的工厂,同时也是管理Bean的仓库。
  • ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangyan_1010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值