当前位置: 首页 > article >正文

【Spring】@PostConstruct详解

在 Java 开发中,尤其是在基于 Spring 框架的项目里,我们常常会遇到需要在对象创建并完成依赖注入后,执行一些初始化操作的场景。@PostConstruct注解正是为解决此类问题而诞生的,它为我们提供了一种便捷且优雅的方式来处理对象的初始化逻辑。@PostConstruct是JSR-250规范定义的注解,用于标记在对象构造完成且依赖注入完成后执行的初始化方法。在Spring框架中的执行顺序为:构造函数 -> @Autowired依赖注入 -> @PostConstruct方法 -> Bean初始化完成

一、@PostConstruct 基础概念

@PostConstruct 是 Java EE 提供的JSR-250规范注解,的注解,作用是标记一个方法,该方法会在对象被创建并且依赖注入完成之后,在构造函数执行完毕后自动调用,无需手动调用。这使得我们能够在对象可用之前,完成一些必要的初始化工作,比如加载配置文件、建立数据库连接、初始化缓存等。

在这里插入图片描述

根据官方代码的注释我们可以看出

  1. @PostConstruct 注解用于需要在完成依赖注入后执行以执行任何初始化的方法。
  2. 所有支持依赖关系注入的类都必须支持此注解。就算注解所在的类不请求注入任何资源,也必须调用带有 @PostConstruct 注释的方法。

使用条件:

  • 只有一个方法可以被@PostConstruct 标注注解(经测试,在Springboot环境中不生效,可以多个方法标记)
  • @PostConstruct 注解的方法不能有参数,除非是在拦截器类中,在这种情况下,它采用 Interceptors 规范定义的 InvocationContext 对象。
    如果是拦截器类上定义的话,方法必须具有以下签名:
    1. void <METHOD>(InvocationContext)

    2. Object <METHOD>(InvocationContext) throws Exception

      注意:PostConstruct 拦截器方法不得引发应用程序异常,但如果相同的拦截器方法除了生命周期事件之外还插入到业务或超时方法上,则可以声明它抛出检查异常,包括 java.lang.Exception。如果 PostConstruct 拦截器方法返回一个值,则容器将忽略该值。

  • 在非拦截器的类上@PostConstruct 注解定义的方法返回值必须是void
  • @PostConstruct 注解的方法访问修饰符可以是 public、protected、package private 或 private,即所有访问级别都可以。
  • @PostConstruct 注解的方法不能用static修饰,除了在Application客户端中。
  • @PostConstruct 注解的方法可以final修饰
  • @PostConstruct 注解的方法不能抛出未经检查的异常(unchecked exception)

二、核心原理与执行顺序

执行顺序

在这里插入图片描述

@PostConstruct用于标记在对象构造完成且依赖注入完成后执行的初始化方法。在Spring框架中的执行顺序为:
构造函数 -> @Autowired依赖注入 -> @PostConstruct方法 -> Bean初始化完成 -> destory方法

源码分析

进入@PostConstruct的源码中,发现只有CommonAnnotationBeanPostProcessor这个类的下面方法用到
在这里插入图片描述

这个方法也很简单,就是把这个注解赋值到CommonAnnotationBeanPostProcessor的父类InitDestroyAnnotationBeanPostProcessor中的initAnnotationType字段
在这里插入图片描述

那么字段又在哪里使用呢? 很巧,只有InitDestroyAnnotationBeanPostProcessor中使用,那我们直接看使用的方法

在这里插入图片描述
buildLifecycleMetadata 方法主要做了

  1. 先判断这个类或类中方法或字段是否有initAnnotationTypedestroyAnnotationType注解
  2. 将该类以及所有父类的所有方法initAnnotationTypedestroyAnnotationType注解的方法,作为参数创建LifecycleElement对象并存入currInitMethodscurrDestroyMethods
  3. 把上面的初始化和销毁方法方法作为参数创建LifecycleMetadata对象

通过上面步骤,我们就可以发现,在Spring中有多个初始化方法是不会报错的,相反而是全部存入currInitMethods集合中,并且所有的父类都会存入这个集合,
在这里插入图片描述

其中在第二步创建LifecycleElement对象时,可以神奇的发现这里,如果方法的参数数量不为0就会抛出异常,这就和使用条件中的@PostConstruct 注解的方法不能有参数相对应了。

那么buildLifecycleMetadata方法又在哪里被使用?
在这里插入图片描述

可以看到这个方法主要是从lifecycleMetadataCache中获取某个类的LifecycleMetadata,如果lifecycleMetadataCache为空,那么就调用最开始的方法,否则就会从lifecycleMetadataCache尝试获取,如果获取不到则通过大名鼎鼎的Double-Check方式,也就是双重检索单例模式,并且使用了ConcurrentHashMap,来防止并发问题,ConcurrentHashMap如何防止并发可看相关文章。

三、使用示例

以下通过一个简单的 Spring Boot 项目示例来展示@PostConstruct的用法。

首先,创建一个普通的 Java 类,并在其中定义一个带有@PostConstruct注解的方法:

@Component
@Slf4j
public class PostConstructTest {
    @Autowired
    private UserMapper userMapper;

    public PostConstructTest() {
        log.info("Constructor");
    }

    @PostConstruct
    public void demo1() {
        if (userMapper != null) {
            log.info("autowired");
        }
        log.info("PostConstruct1");
    }

    @PostConstruct
    public void demo2() {
        log.info("PostConstruct2");
    }
}

然后,启动 Spring Boot 应用程序,观察控制台输出:

2025-03-12 22:40:48.529   c.a.mpdemo1010.config.PostConstructTest  : Constructor
2025-03-12 22:40:49.378   c.a.mpdemo1010.config.PostConstructTest  : autowired
2025-03-12 22:40:49.378  c.a.mpdemo1010.config.PostConstructTest  : PostConstruct1
2025-03-12 22:40:49.378   c.a.mpdemo1010.config.PostConstructTest  : PostConstruct2

从输出结果可以清晰地看到,构造函数先被调用,随后@PostConstruct注解的方法被调用。这表明@PostConstruct注解的方法确实是在对象创建和依赖注入完成之后执行的。


http://www.kler.cn/a/583076.html

相关文章:

  • Conda:CondaSSLError
  • LabVIEW VI Scripting实现连接器窗格自动化
  • varchar (255) varchar (2550) 在mysql中实际占的空间会是十倍吗
  • MySQL的安装、备份还原及主从同步
  • java设计模式之桥接模式
  • 深度学习GRU模型原理
  • Linux——Shell运行原理以及Linux权限
  • 【Linux docker 容器】关于想要让虚拟机在开机时候也docker自己启动,容器也自己启动,省去要自己开docker和容器
  • 已安装 MFC 仍提示“此项目需要 MFC 库”的解决方法 (MSB8041)
  • 骑士74CMS_v3.34.0SE版uniapp全开源小程序怎么编译admin和member流程一篇文章说清楚
  • 【Go语言圣经1.5】
  • 前端对话框项目——调用字节Coze API
  • 18 | 实现简洁架构的 Handler 层
  • python str repr方法区别
  • 数据库原理4
  • 开源链动2+1模式AI智能名片S2B2C商城小程序在KOC参与门店做透中的应用探索
  • 本地部署资源聚合搜索神器 Jackett 并实现外部访问
  • 苹果“被盗设备保护”的取证意义
  • Haproxy配置入门
  • 在芯片设计的后端流程中,通过metal修timing是什么意思,怎么实施。举个timing违例说明一下