Spring容器启动时执行代码(数据预热)


在Java项目中,有时我们需要在应用启动时执行一些初始化代码,比如加载配置、初始化数据库连接池、预热数据等。这些操作对于应用的顺利运行至关重要。以下是一些在Java项目中实现启动时执行代码(预热数据)的常用方式:
SpringBoot:CommandLineRunner和ApplicationRunner。
Spring 提供了接口 InitializingBean,@PostConstruct
静态代码块

静态代码块

@Slf4j
@Component
public class ServletContextListenerImpl {
    /**
     * 静态代码块会在依赖注入后自动执行,并优先执行
     */
    static {
        log.info("Spring容器启动时自动执行静态代码块!");
    }
}

@PostConstruct

@PostConstruct与@PreDestroy
值得注意的是,这两个注解不属于Spring,它们是源于JSR-250中的两个注解,位于common-annotations.jar中
@PostConstruct注解用于标注在Bean被Spring初始化之前需要执行的方法
@PreDestroy注解用于标注Bean被销毁前需要执行的方法
上面提到过, @PostConstruct 可以在Servlet初始化之前加载一些缓存数据,如:预热数据字典,读取properties配置文件,
使用Redis进行的数据预热,需要项目启动以后,触发第一次调用才能生成缓存,而利用 @PostConstruct 注解能让预热数据在Bean初始化阶段完成,比Redis更早。
@Component+@PostCostruct完成预热

@Slf4j
@Configuration
public class BeanConfiguration {
    @Autowired
    private BusinessService businessService;
    // 模拟预热的数据
    private static String mysql_data;
    @PostConstruct
    public void construct(){
        log.info("〓〓〓〓〓〓〓〓〓〓 Autowired 加载完成!!");
        mysql_data = businessService.demo5();
        log.info("〓〓〓〓〓〓〓〓〓〓 mysql_data = " + mysql_data);
    }
}

InitialzingBean

当一个类实现这个接口之后,Spring启动后,初始化Bean时,若该Bean实现InitialzingBean接口,会自动调用afterPropertiesSet()方法,完成一些用户自定义的初始化操作。
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。注意,实现该接口的最好加上Spring的注解注入,比如@Component。
实现 InitializingBean 接口并重写 afterPropertiesSet 方法,可以在 Spring Bean 初始化完成后执行缓存预热,具体实现代码如下

@Component
public class CachePreloader implements InitializingBean {
    @Autowired
    private YourCacheManager cacheManager;
    @Override
    public void afterPropertiesSet() throws Exception {
        // 执行缓存预热业务...
        cacheManager.put("key", dataList);
    }
}

CommandLineRunner和ApplicationRunner

Spring boot的CommandLineRunner接口主要用于实现在应用初始化后,去执行一段代码块逻辑,这段初始化代码在整个应用生命周期内只会执行一次
@Component可配合CommandLineRunner使用,在程序启动后执行一些基础任务
使用方式:
@Component 注解配合 @SpringBootApplication注解一起使用

@Component
public class InitCompetition implements CommandLineRunner {
    private static Log log = LogFactory.get();
 
    @Override
    public void run(String... args) throws Exception {
        Date date = new Date();
        //初始化活动
        String sql = "update competition set status = ?  where status != ?";
        Db.use().execute(sql, CompetitionStatus.FINISHED.name(),CompetitionStatus.FINISHED.name());
        log.info("数据库初始化完成");
    }
}

Spring Boot应用程序在启动后,会遍历CommandLineRunner接口的实例并运行它们的run方法。也可以利用@Order注解(或者实现Order接口)来规定所有CommandLineRunner实例的运行顺序。
如下我们使用@Order 注解来定义执行顺序。
@Order 注解的执行优先级是按value值从小到大顺序。

@Component
@Order(value=1)
public classMyStartupRunner2 implements CommandLineRunner {
    @Override
    publicvoid run(String...args) throws Exception {
        System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作 22222222<<<<<<<<<<<<<");
    }
}

@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 执行缓存预热业务...
        cacheManager.put("key", dataList);
    }
}

CommandLineRunner 和 ApplicationRunner 区别如下:
方法签名不同:
CommandLineRunner 接口有一个 run(String… args) 方法,它接收命令行参数作为可变长度字符串数组。
ApplicationRunner 接口则提供了一个 run(ApplicationArguments args) 方法,它接收一个 ApplicationArguments 对象作为参数,这个对象提供了对传入的所有命令行参数(包括选项和非选项参数)的访问。
参数解析方式不同:
CommandLineRunner 接口更简单直接,适合处理简单的命令行参数。
ApplicationRunner 接口提供了一种更强大的参数解析能力,可以通过 ApplicationArguments 获取详细的参数信息,比如获取选项参数及其值、非选项参数列表以及查询是否存在特定参数等。
使用场景不同:
当只需要处理一组简单的命令行参数时,可以使用 CommandLineRunner。
对于需要精细控制和解析命令行参数的复杂场景,推荐使用 ApplicationRunner。

ServletContextListener

通过实现ServletContextListener接口,可以在Web应用启动和关闭时执行相应的代码。

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AppContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 初始化代码
        System.out.println("Web应用启动时执行代码");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 清理代码
        System.out.println("Web应用关闭时执行代码");
    }
}

执行顺序

static>@PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner
@Component
public class Test implements InitializingBean, ApplicationRunner, CommandLineRunner {

    @PostConstruct
    public void init(){
        System.out.println("PostConstruct 方法执行");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean 方法执行");
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("这个是测试ApplicationRunner接口");

    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("这个是测试CommandLineRunn接口");
    }
}

运行结果:
PostConstruct 方法执行 InitializingBean 方法执行
这个是测试ApplicationRunner接口 这个是测试CommandLineRunn接口
由此可知: @PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner,当然如果涉及到类中的static代码块,则是:
static>@PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner
@PostConstruct、InitializingBean、ApplicationRunner、CommandLineRunner和ServletContextListener都是用于在应用程序初始化和启动阶段执行特定操作的机制,但它们存在一些区别:

  1. @PostConstruct:这是一个注解,用于标记在依赖注入完成后立即执行的方法。@PostConstruct方法在bean的构造函数执行完成后自动调用,用于执行一些初始化操作。它是Spring框架提供的一种方式。
  2. InitializingBean:这是一个接口,实现了它的类可以在依赖注入完成后进行自定义初始化。通过实现afterPropertiesSet()方法,可以在Spring容器创建bean并完成属性注入后进行初始化操作。它是Spring框架提供的一种方式。
  3. ApplicationRunner:这是Spring Boot提供的一个接口,类似于main方法,用于在Spring Boot应用程序启动后运行特定的代码块。通过实现run()方法,在应用程序启动后执行自定义逻辑。它通常用于在应用程序完全启动后执行某些任务,例如加载数据或者启动定时任务。
  4. CommandLineRunner:这也是Spring Boot提供的一个接口,类似于ApplicationRunner,但还允许访问命令行参数。通过实现run()方法,并使用String[] args参数,可以在应用程序启动后执行特定逻辑,并使用命令行参数进行配置。
  5. ServletContextListener:这是Java Servlet规范中定义的接口,用于接收关于ServletContext的生命周期事件通知。通过实现contextInitialized()和contextDestroyed()方法,可以在Web应用程序启动和关闭时执行某些操作,例如初始化资源或释放资源。与前面提到的机制不同,它是针对Web应用程序的。
    总结来说,@PostConstruct和InitializingBean是Spring框架提供的方式,用于在Spring容器中进行bean的初始化操作;ApplicationRunner和CommandLineRunner是Spring Boot提供的方式,在Spring Boot应用程序启动后执行定制化的逻辑;而ServletContextListener是Servlet规范中定义的,在Web应用程序启动和关闭时执行特定操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

思静语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值