springboot3生命周期监听的使用和源码解析

    定义SpringApplicationRunListener来监听springApplication的启动

1.通过实现springApplicationRunListener来实现监听。

2.在 META-INF/spring.factories 中配置 org.springframework.boot.SpringApplicationRunListener=自己的Listener。

在默认的springboot配置中就有给我们配置了事件监听器。

 配置了EvenPublishRunListener。

 自定义myListener类。

import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

import java.time.Duration;

public class myListenter implements SpringApplicationRunListener {
    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        SpringApplicationRunListener.super.starting(bootstrapContext);
        System.out.println("starting");
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        SpringApplicationRunListener.super.environmentPrepared(bootstrapContext, environment);
        System.out.println("environmentPrepared");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        SpringApplicationRunListener.super.contextPrepared(context);
        System.out.println("contextPrepared");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        SpringApplicationRunListener.super.contextLoaded(context);
        System.out.println("contextLoaded");
    }

    @Override
    public void started(ConfigurableApplicationContext context, Duration timeTaken) {
        SpringApplicationRunListener.super.started(context, timeTaken);
        System.out.println("started");
    }

    @Override
    public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
        SpringApplicationRunListener.super.ready(context, timeTaken);
        System.out.println("ready");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        SpringApplicationRunListener.super.failed(context, exception);
        System.out.println("failed");
    }
}

在/META-INF/spring.factories。

org.springframework.boot.SpringApplicationRunListener=com.huang.listenter.myListenter

启动测试,效果为下:

 SpringApplicationRunListener作用位置的源码分析

starting :应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行。(此时ioc容器还没有被启动)

调用位置: 

  environmentPrepared: 环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;【调一次】 

调用位置: 

 contextPrepared:ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建  【调一次】(看参数就可以推出现在要进行ioc启动时的配置)

 调用位置(此时创建了ioc容器):

在ioc创建好并准备好后将 BootstrapContext关闭。

 contextLoaded:ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(我们的bean没创建)。

 调用位置:

 且在此方法要在刷新ioc容器(将bean配置到ioc容器中)之前。

此方法就是刷新容器。(在此过程中就会自动创建服务器类,并将其配置到ioc容器中) 

started:ioc容器刷新了(所有bean造好了),但是 runner 没调用。

调用位置: 

 ready:ioc容器刷新了(所有bean造好了),所有 runner 调用完了。

 调用位置:

 总结

重点掌握流程和各个方法的作用即可。 

事件触发时机

    各种回调监听器

  • BootstrapRegistryInitializer 感知特定阶段感知引导初始化
    • META-INF/spring.factories
    • 创建引导上下文bootstrapContext的时候触发。
    • application.addBootstrapRegistryInitializer();
    • 场景:进行密钥校对授权。
  • ApplicationContextInitializer: 感知特定阶段 感知ioc容器初始化
    • META-INF/spring.factories
    • application.addInitializers();
  • ApplicationListener: 感知全阶段基于事件机制,感知事件。 一旦到了哪个阶段可以做别的事
    • @Bean@EventListener事件驱动
    • SpringApplication.addListeners(…)SpringApplicationBuilder.listeners(…)
    • META-INF/spring.factories
  • SpringApplicationRunListener: 感知全阶段生命周期 + 各种阶段都能自定义操作; 功能更完善。
    • META-INF/spring.factories
  • ApplicationRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪(用于感知Runner的运行情况)
    • @Bean
  • CommandLineRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪(用于感知Runner的运行情况)
    • @Bean

最佳实战:

  • 如果项目启动前做事: BootstrapRegistryInitializerApplicationContextInitializer
  • 如果想要在项目启动完成后做事:ApplicationRunnerCommandLineRunner
  • 如果要干涉生命周期做事:SpringApplicationRunListener
  • 如果想要用事件机制:ApplicationListener 

 编写ApplicationListener实现类。

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

public class myEventListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("触发事件:" + event);
    }
}

在spring.factories中配置ApplicationListener。

org.springframework.context.ApplicationListener=com.huang.listenter.myEventListener

运行结果为下:

通过结果分析我们可以得到下图:

9大事件触发顺序&时机

  1. ApplicationStartingEvent:应用启动但未做任何事情, 除过注册listeners and initializers.
  2. ApplicationEnvironmentPreparedEvent: Environment 准备好,但context 未创建.
  3. ApplicationContextInitializedEvent: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何bean未加载
  4. ApplicationPreparedEvent: 容器刷新之前,bean定义信息加载
  5. ApplicationStartedEvent: 容器刷新完成, runner未调用

=========以下就开始插入了探针机制============

  1. AvailabilityChangeEventLivenessState.CORRECT应用存活; 存活探针
  2. ApplicationReadyEvent: 任何runner被调用
  3. AvailabilityChangeEventReadinessState.ACCEPTING_TRAFFIC就绪探针,可以接请求
  4. ApplicationFailedEvent :启动出错

事件驱动开发

例子:登录功能,登录成功后调用 赠送优惠劵一张,积分加1。

 实现步骤:

  在登录成功后,新建一个自定义的特定事件,通过事件发布者将此事件发布,需要被调用的service会通过监听该特定的事件,来执行对应的方法,也就是一监听到特定的事件就进行对应的方法。

1.创建自定义事件:Loginevent

import com.huang.pojo.User;
import org.springframework.context.ApplicationEvent;

//创建自定义的事件
public class LoginEvent extends ApplicationEvent {

    public LoginEvent(User user) {
        super(user);
    }
}

2.创建一个自定义的事件发布类:EventPublisher

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

@Service
public class EventPublisher implements ApplicationEventPublisherAware {

    ApplicationEventPublisher applicationEventPublisher;
    //发布事件
    public void eventSend(ApplicationEvent applicationEvent) {
        applicationEventPublisher.publishEvent(applicationEvent);
    }

    @Override
    //ApplicationEventPublisherAware自动调用此方法,此方法中的ApplicationEventPublisher会自动从ioc容器中注入
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

3.创建三个service,分别去监听对应的事件

 LoginService:

import com.huang.event.EventPublisher;
import com.huang.event.LoginEvent;
import com.huang.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LoginService {

    //在登录成功后,新建一个自定义的特定事件,通过事件发布者将此事件发布,需要被调用的service会通过监听该特定的事件,
    // 来执行对应的方法,也就是一监听到特定的事件就进行对应的方法
    //1.创建自定义事件
    //2.创建一个自定义的事件发布类
    //3.创建三个service,分别去监听对应的事件
    @Autowired
    EventPublisher eventPublisher;

    public void LoginSuccess() {
        LoginEvent loginEvent = new LoginEvent(new User("秃狼", "123456"));
        eventPublisher.eventSend(loginEvent);
    }

}

 ticketService:

import com.huang.event.LoginEvent;
import com.huang.pojo.User;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
public class ticketService {

    @EventListener
    public void discoverEvent(LoginEvent loginEvent) {
        User user = (User) loginEvent.getSource();
        this.addTicket(user);
    }

    public void addTicket(User user) {
        System.out.println(user.getName() + "优惠劵加一");
    }
}

 totalService:

import com.huang.event.LoginEvent;
import com.huang.pojo.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;

@Service
public class totalService implements ApplicationListener<LoginEvent> {

    public void addTotal(User user) {
        System.out.println(user.getName() + "积分加一");
    }

    @Override
    public void onApplicationEvent(LoginEvent event) {
        User user = (User) event.getSource();
        this.addTotal(user);
    }
}

在测试类中自动注入LoginService进行测试,测试结果为下:

可以看到我两个service都被调用了,通过都是通过监听对应的事件。通过监听事件来实现业务的好处就是:不再需要再主Service中调用各个其他的service,在后续添加业务时只需要监听对应的事件即调用对应的service,不会修改到原代码,符合代码规范,大大的实现了解耦合

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值