由缓存在服务启动时自动获取数据库中的数据而引申出的服务启动时如何自动触发任务,浅谈

springboot服务启动时,自动触发任务

作为浅谈就不深挖底层源码,具体源码可详见其他博客,都有贴出源码的具体分析,本文仅作为个人的学习心得分享。

1.从bean下手

①afterPropertiesSet

afterPropertiesSet()需要实现InitializingBean接口,到此实例化完成,进入生存期。
示例:

@Component
public class CacheTask implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行输出b" + "---" + new Date());
    }

}

②postProcessBeforeInitialization与postProcessAfterInitialization

postProcessBeforeInitialization和postProcessAfterInitialization这两个方法皆为BeanPostProcessor接口下的方法。
postProcessBeforeInitialization和postProcessAfterInitialization这两个方法分别是在Spring容器中的bean初始化(bean属性配置完成)前后执行,所以Spring容器中的每个bean对象初始化前后,都会执行BeanPostProcessor接口的两个方法。
示例:

@Component
public class BeanTest implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行输出A" + "---" + new Date());
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行输出B" + "---" + new Date());
        return bean;
    }
}

③@PostConstruct注解

@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法只会被服务器执行一次。
spring的Bean在创建的时候会进行初始化,而初始化过程会解析出@PostConstruct注解的方法,并反射调用该方法。从而,在启动的时候该方法被执行了。
spring中Constructor、@Autowired、@PostConstruct的顺序
Constructor >> @Autowired >> @PostConstruct
注意:子类实例化过程中会调用父类中的@PostConstruct方法!
示例:

@Component
public class CacheTask {

    @PostConstruct
    public void CacheTaskInit() {
        System.out.println("执行输出A" + "---" + new Date());
    }
}

④init-method

init-method在springMVC时期需要在web.xml文件里进行配置,而springboot项目中就是用约定的注解来替换配置。
示例:

@Component
public class CacheTask {

    public void initMethod() {
        System.out.println("initMethod");
    }
}

另还需添加一个配置类

@Configuration
public class BeanConfig {@Bean(initMethod = "initMethod")
    public CacheTask cacheTask(){
        return new CacheTask ();
    }
}

相同点:@PostConstruct和afterPropertiesSet都是在bean的属性注入完毕之后才执行,都可以用来进行bean 的初始化
区别:@PostConstruct是通过BeanPostProcessor的前置方法中执行的(我们也可以参考这个自定义注解实现),略早于通过继承InitializingBean的afterPropertiesSet初始化方法。而afterPropertiesSet早于Init-Method。
postProcessBeforeInitialization与postProcessAfterInitialization是每个bean初始化(bean属性配置完成)即InitializingBean前后执行的,因此对于整个服务来说有多少个bean便会执行多少次。

只需执行一次的任务建议使用@PostConstruct或afterPropertiesSet。
更容易理解的话就是@PostConstruct和afterPropertiesSet只对你所交给spring容器托管的这个类进行bean初始化的时候调用,而postProcessBeforeInitialization与postProcessAfterInitialization是对每个bean进行负责。

2.从spring容器下手

①ApplicationListener

总的来说,是监听spring容器的事件来实现,类似于上文从bean入手,就是在其某阶段监听到该事件进行任务的触发。
示例:
ApplicationStartedEvent

//监听ApplicationStarted
@Component
public class StartListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("started listener");
    }
}

ApplicationReadyEvent

@Component
public class ReadyListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        System.out.println("Ready Listener");
    }
}

ContextRefreshedEvent
ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用

@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println("ContextRefreshed Listener");
    }
}

②CommandLineRunner

示例:

@Component
public class CommandLineRunnerTest implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("执行CommandLineRunner方法");
    }
}

③ApplicationRunner

示例:

@Component
public class ApplicationRunnerTest implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("执行ApplicationRunner方法");
    }
}

为了理清上述方案的运行顺序,我们须知Spring boot运行时,会发送以下事件

  1. ApplicationStartingEvent

  2. ApplicationEnvironmentPreparedEvent:当Environment已经准备好,在context 创建前

  3. ApplicationContextInitializedEvent:在ApplicationContext 创建和ApplicationContextInitializer都被调用后,但是bean definition没有被加载前

  4. ApplicationPreparedEvent:bean definition已经加载,但是没有refresh

  5. ApplicationStartedEvent: context 已经被refresh, 但是application 和command-line 的runner都没有被调用

  6. AvailabilityChangeEvent

  7. ApplicationReadyEvent: application 和command-line 的runner都被调用后触发

  8. AvailabilityChangeEvent

  9. ApplicationFailedEvent: 启动失败触发
    总结:在ApplicationPreparedEvent之后和ApplicationStartedEvent之前发送ContextRefreshedEvent
    ContextRefreshedEvent可能多次执行因为web应用会出现父子容器,这样就会触发两次
    CommandLineRunner、ApplicationRunner 接口是在容器启动成功(设置并刷新ApplicationContext)后的最后一步回调,可以通过@Order注解(属性指定数字越小表示优先级越高)或者Ordered接口来控制执行顺序,如果 Order 相同,那么 ApplicationRunner 先执行。
    这两个接口都以相同的方式工作,并提供一个单独的运行方法,在springapplication.run(.)完成之前,它将被调用。ApplicationRunner 和 CommandLineRunner 是 Spring Boot 提供的专门用于处理启动后的初始化工作的接口,他们的执行一定是在容器启动的最后一步。也就是 run 方法的最后一步。
    CommandLineRunner接口提供对应用程序参数的访问,作为一个简单的字符串数组,ApplicationRunner提供了ApplicationArguments。用更容易理解的话来说,两者参数都为Spring Boot 入口的传参,即为springboot启动类main函数内的args参数。CommandLineRunner的参数是最原始的参数,没有做任何处理。ApplicationRunner的参数是ApplicationArguments,是对原始参数做了进一步的封装,ApplicationArguments对象可以解析–name=value形式。
    执行顺序在SpringApplication启动之前,在大部分监听器之后。

3.从ServletContext下手

ServletContext,是一个全局的储存信息的空间,服务器开始,其就存在,服务器关闭,其才释放。

①setServletContext

示例:

@Component
public class ServletContextTest implements ServletContextAware {
    /**
     * 在填充普通bean属性之后但在初始化之前调用
     * 由此可见比@PostConstruct与afterPropertiesSet调用都要早
     * 类似于initializingbean的afterpropertiesset或自定义init方法的回调
     *
     */
    @Override
    public void setServletContext(ServletContext servletContext) {
        System.out.println("调用了setServletContext");
    }
}

②contextInitialized

类似于上文中的监听spring容器的事件,只不过这个方法只是监听ServletContext初始化的事件
示例:

@Component
public class ServletContextListenerTest implements ServletContextListener {
    /**
     *监听ServletContext初始化事件
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("setServletContextInit Listener");
    }
}

setServletContext在contextInitialized之前调用。注:contextInitialized在@PostConstruct之前调用
从程序进程角度来说,很明显setServletContext在ServletContext初始化之前。

注:上类方法都需要@Component等注解交给spring容器进行管理配置

若文中有误,欢迎大家指出并进行评判与斧正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值