了解Spring 容器的生命周期,我们可以在容器生命周期的各个阶段执行自定义的操作以拓展应用程序的能力。本片博客的所有源代码,可以在 boot-init 仓库找到。有兴趣的自行查阅。
2019-09-02 22:47:43.015 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
2019-09-02 22:47:43.101 INFO 61026 --- [ main] com.rainstorm.boot.skyLog : [ApplicationInit][initialize][][] - ; message: 初始化方法调用
2019-09-02 22:47:43.102 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.boot.context.event.ApplicationContextInitializedEvent
...
2019-09-02 22:47:43.149 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.boot.context.event.ApplicationPreparedEvent
...
2019-09-02 22:47:49.036 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : me.rainstorm.boot.lifecycle.application.JacksonConverterConfigDoneEvent
...
2019-09-02 22:47:50.041 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.context.event.ContextRefreshedEvent
...
2019-09-02 22:47:50.061 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
2019-09-02 22:47:50.065 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.boot.context.event.ApplicationStartedEvent
2019-09-02 22:47:50.076 INFO 61026 --- [ main] m.r.b.l.a.ApplicationEventTest : org.springframework.boot.context.event.ApplicationReadyEvent
...
2019-09-02 22:50:41.951 INFO 61026 --- [ Thread-1] m.r.b.l.application.ApplicationInit : from shutdown hook
2019-09-02 22:50:41.951 INFO 61026 --- [ Thread-18] m.r.b.l.a.ApplicationEventTest : org.springframework.context.event.ContextClosedEvent
// todo 周末再搞吧
// todo 详情周末搞
// todo 详情周末搞
// todo 事件的发布订阅模式
借助 ApplicationContext
实现自定义事件发布与监听
监听器事件
@Slf4j
@Component
public class EventPublisherImpl implements ApplicationContextAware, ApplicationEventPublisher {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void publishEvent(Object event) {
applicationContext.publishEvent(event);
}
}
自定义事件
public class JacksonConverterConfigDoneEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public JacksonConverterConfigDoneEvent(Object source) {
super(source);
}
}
发布事件
@Configuration
public class JacksonConverter {
@Resource
private ApplicationEventPublisher eventPublisher;
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
....
eventPublisher.publishEvent(new JacksonConverterConfigDoneEvent(objectMapper));
return objectMapper;
}
@Slf4j
public class ApplicationInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final String CATEGORY = ApplicationInit.class.getSimpleName();
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
final String logMethodName = "initialize";
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("from shutdown hook");
}));
LogUtil.info(LogBuilder.init(CATEGORY, logMethodName).setMessage("初始化方法调用").build());
}
}
注册 ShutdownHook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("from shutdown hook");
}));
注意,因为 ShutdownHook 底层使用Map 作为存储结构,且是多线程并发执行,无法精确的指定执行的先后顺序,除非显示的对 Shutdown hook 进行同步处理
static void runHooks() {
Collection<Thread> threads;
synchronized(ApplicationShutdownHooks.class) {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
}
for (Thread hook : threads) {
while (true) {
try {
hook.join();
break;
} catch (InterruptedException ignored) {
}
}
}
}