要在 Spring IoC 容器构建完毕之后执行一些逻辑,怎么实现

要在 Spring IoC 容器构建完毕之后执行一些逻辑,怎么实现

在八年的 Java 开发历程中,Spring 框架一直是我工作中的得力助手。其中 IoC(控制反转)容器是 Spring 的核心特性,它帮我们管理对象的生命周期和依赖关系。很多时候,我们需要在 Spring IoC 容器构建完毕之后执行一些特定逻辑,比如数据初始化、资源加载、服务注册等。接下来,我将从原理入手,结合实际业务场景,通过核心代码演示实现方式。

一、Spring IoC 容器原理简述

Spring IoC 容器就像是一个巨大的对象工厂,负责创建、配置和管理 Bean。在容器启动过程中,它会解析配置信息(XML 配置文件或注解配置),实例化 Bean 并建立它们之间的依赖关系。容器构建的过程大致分为几个阶段:资源定位、Bean 定义的加载与解析、Bean 的实例化和初始化等。理解这些阶段,对我们实现在容器构建完毕后执行逻辑至关重要,因为我们需要找到一个合适的时机,让自定义逻辑介入。

二、常见业务场景

1. 数据初始化

在系统启动时,可能需要从数据库加载一些基础数据,比如字典表数据、系统配置项等,将其缓存到内存中,供后续业务逻辑快速访问。以电商系统为例,商品分类信息、支付方式等基础数据,就可以在容器构建完毕后加载到缓存中。

2. 资源加载

有些业务需要加载外部资源,如读取配置文件、加载模板文件等。像报表生成模块,在系统启动时需要加载报表模板文件,为后续生成报表做准备。

3. 服务注册

在分布式系统中,服务提供者需要将自己注册到注册中心。例如基于 Spring Cloud 的微服务架构,服务启动后,要在容器构建完成后向 Eureka、Nacos 等注册中心注册服务实例,以便服务消费者能够发现和调用。

三、实现方式及核心代码

1. 实现 ApplicationContextAware 接口

ApplicationContextAware 接口提供了获取 ApplicationContext(Spring IoC 容器的高级抽象)的能力,我们可以通过重写其 setApplicationContext 方法,在容器初始化过程中获取到容器实例,并在容器构建完毕后执行逻辑。

java 复制代码
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class MyLogicAfterIoc implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        // 在这里执行自定义逻辑
        System.out.println("Spring IoC容器已构建完毕,执行自定义逻辑");
    }
}

这种方式能获取到容器实例,但 setApplicationContext 方法在容器初始化过程中就会被调用,若要确保所有 Bean 都已初始化完毕再执行逻辑,还需额外判断。

2. 使用 @PostConstruct 注解

@PostConstruct 注解标注的方法会在 Bean 的依赖注入完成后被自动调用,对于单个 Bean 的初始化逻辑很方便。

kotlin 复制代码
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
@Component
public class SingleBeanInitialization {
    @PostConstruct
    public void init() {
        System.out.println("单个Bean初始化完成,执行相关逻辑");
    }
}

不过它只能作用于单个 Bean,如果要在整个容器构建完毕后执行全局逻辑,就不太适用了。

3. 实现 SmartLifecycle 接口

SmartLifecycle 接口是 Lifecycle 接口的扩展,提供了更精细的生命周期控制。实现该接口并重写相关方法,可以在容器启动完成后执行逻辑。

typescript 复制代码
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
@Component
public class MySmartLifecycle implements SmartLifecycle {
    private boolean running = false;
    @Override
    public void start() {
        System.out.println("Spring IoC容器构建完毕,执行SmartLifecycle的start逻辑");
        running = true;
    }
    @Override
    public void stop() {
        running = false;
    }
    @Override
    public boolean isRunning() {
        return running;
    }
    @Override
    public int getPhase() {
        return 0;
    }
}

start 方法会在容器启动完成后被调用,getPhase 方法用于指定启动和停止的顺序,数值越小越先启动,越晚停止。

4. 实现 ApplicationListener 接口

ContextRefreshedEvent 事件会在 Spring 应用上下文初始化或刷新完成时发布,通过监听该事件,可以在容器构建完毕后执行逻辑。

typescript 复制代码
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) {
            // 根应用上下文才执行逻辑,避免父子容器重复执行
            System.out.println("Spring IoC容器构建完毕,执行ContextRefreshedEvent监听逻辑");
        }
    }
}

这里通过判断 event.getApplicationContext().getParent() == null 来确保只在根应用上下文触发时执行逻辑,防止父子容器环境下逻辑被多次执行。

四、选择合适的实现方式

上述几种方式各有优劣,实际开发中要根据具体场景选择:

  • 如果需要获取 ApplicationContext 实例,并且对执行时机要求不那么严格,可选择实现 ApplicationContextAware 接口。
  • 对于单个 Bean 的初始化逻辑,@PostConstruct 注解简单便捷。
  • 涉及容器生命周期的精细控制,SmartLifecycle 接口是不错的选择。
  • 若要监听整个容器初始化完成事件,实现 ApplicationListener 接口最为合适。
相关推荐
重庆小透明2 小时前
【从零开始学习JVM | 第六篇】运行时数据区
java·jvm·后端·学习
你的人类朋友4 小时前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴4 小时前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___4 小时前
日期的数据格式转换
前端·后端·学习·node.js·node
保持学习ing6 小时前
SpringBoot前后台交互 -- 登录功能实现(拦截器+异常捕获器)
java·spring boot·后端·ssm·交互·拦截器·异常捕获器
十年老菜鸟7 小时前
spring boot源码和lib分开打包
spring boot·后端·maven
白宇横流学长7 小时前
基于SpringBoot实现的课程答疑系统设计与实现【源码+文档】
java·spring boot·后端
加瓦点灯8 小时前
什么?工作五年还不了解SafePoint?
后端
他日若遂凌云志9 小时前
Lua 模块系统的前世今生:从 module () 到 local _M 的迭代
后端
David爱编程9 小时前
Docker 安全全揭秘:防逃逸、防漏洞、防越权,一篇学会容器防御!
后端·docker·容器