定义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
最佳实战:
- 如果项目启动前做事:
BootstrapRegistryInitializer
和ApplicationContextInitializer
- 如果想要在项目启动完成后做事:
ApplicationRunner
和CommandLineRunner
- 如果要干涉生命周期做事:
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大事件
触发顺序&时机
ApplicationStartingEvent
:应用启动但未做任何事情, 除过注册listeners and initializers.ApplicationEnvironmentPreparedEvent
: Environment 准备好,但context 未创建.ApplicationContextInitializedEvent
: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何bean未加载ApplicationPreparedEvent
: 容器刷新之前,bean定义信息加载ApplicationStartedEvent
: 容器刷新完成, runner未调用
=========以下就开始插入了探针机制============
AvailabilityChangeEvent
:LivenessState.CORRECT
应用存活; 存活探针ApplicationReadyEvent
: 任何runner被调用AvailabilityChangeEvent
:ReadinessState.ACCEPTING_TRAFFIC
就绪探针,可以接请求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,不会修改到原代码,符合代码规范,大大的实现了解耦合。