这里写目录标题
- 1. 前言
- 2. springboot自带的11个监听器
- 2.1 `org.springframework.boot.ClearCachesApplicationListener`
- 2.2 `org.springframework.boot.builder.ParentContextCloserApplicationListener`
- 2.3 `org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor`
- 2.4 `org.springframework.boot.context.FileEncodingApplicationListener`
- 2.5 `org.springframework.boot.context.config.AnsiOutputApplicationListener`
- 2.6 `org.springframework.boot.context.config.ConfigFileApplicationListener`**重要**
- 2.7 `org.springframework.boot.context.config.DelegatingApplicationListener`
- 2.8 `org.springframework.boot.context.logging.ClasspathLoggingApplicationListener`
- 2.9 `org.springframework.boot.context.logging.LoggingApplicationListener`
- 2.10 `org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener`
- 2.11 `org.springframework.boot.autoconfigure.BackgroundPreinitializer`
1. 前言
在前两个篇章的学习中,我们已经了解到在springboot启动过程中,使用了观察者模式。在启动过程的不同时刻,通过EventPublishingRunListener
发布相应的事件,最终相应的监听器执行相应的方法。本篇学习一下启动过程中的11个监听器。
2. springboot自带的11个监听器
在springboot源码解析(1)一个springboot程序的启动及简单分析中,我们已经介绍过ApplicationListener
的实例化时机。那么springboot到底有哪些监听器会被实例化?
在org.springframework.boot:spring-boot
包的META-INF/spring.factories中,有10个。
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
在在org.springframework.boot:spring-boot-autoconfigure
包的META-INF/spring.factories中,有1个。
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
类名 | 功能 | 监听事件 |
---|---|---|
org.springframework.boot.ClearCachesApplicationListener | 每次容器刷新前,清除启动过程和类加载器中的缓存 | ContextRefreshedEvent |
org.springframework.boot.builder.ParentContextCloserApplicationListener | 父容器关闭后,这个监听器回去关闭子容器 | 监听父容器的关闭时间 |
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor | 针对CloudFoundryVcap环境变量的后置处理器 | ApplicationPreparedEvent |
org.springframework.boot.context.FileEncodingApplicationListener | 检查jvm系统参数和sping中设置的文件编码是不是一致 | ApplicationEnvironmentPreparedEvent |
org.springframework.boot.context.config.AnsiOutputApplicationListener | 在环境准备好后,设置spring.output.ansi 的相关属性,可以用来控制是否彩色输出。 | ApplicationEnvironmentPreparedEvent |
org.springframework.boot.context.config.ConfigFileApplicationListener | 主要是监听ApplicationEnvironmentPreparedEvent (环境准备完成)和ApplicationPreparedEvent (spring容器准备好,但还没有刷新)事件,执行相应的环境后置处理器,添加一个BeanFactoryPostProcess到容器中。只是也作为一个环境后置处理器发挥作用 | 监听ApplicationEvent事件 |
org.springframework.boot.context.config.DelegatingApplicationListener | 代理监听器,如果你新写了一个监听器,如果想要在启动过程过程中发挥作用。有两个办法,一是在spring.factories中配置;二是在spring的context.listener.classes属性中配置,这个配置中的监听器会被DelegatingApplicationListener处理 | ApplicationEvent |
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener | 日志输出classpath下的文件 | ApplicationEnvironmentPreparedEvent 和ApplicationFailedEvent |
org.springframework.boot.context.logging.LoggingApplicationListener | 进行日志系统的初始化 | ApplicationEvent |
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener | 不知道干啥的 | |
org.springframework.boot.autoconfigure.BackgroundPreinitializer | 把一些耗时的初始化任务从启动springboot的前台线程放到后台线程执行 | SpringApplicationEvent |
2.1 org.springframework.boot.ClearCachesApplicationListener
每次容器重新刷新之前,需要删除启动过程中和加载器器的缓存。
class ClearCachesApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 清除反射工具所使用的缓存
// ReflectionUtils 是一个Spring内部反射处理工具,它在工作过程中
// 对反射操作中遇到的方法和属性都有可能做缓存处理,它的clearCache()方法
// 是清除这些缓存
ReflectionUtils.clearCache();
// 清除classLoader及其祖先classLoader所使用的缓存(如果他们使用了缓存的话)
clearClassLoaderCaches(Thread.currentThread().getContextClassLoader());
}
private void clearClassLoaderCaches(ClassLoader classLoader) {
if (classLoader == null) {
return;
}
try {
Method clearCacheMethod = classLoader.getClass().getDeclaredMethod("clearCache");
clearCacheMethod.invoke(classLoader);
}
catch (Exception ex) {
// Ignore
}
clearClassLoaderCaches(classLoader.getParent());
}
}
2.2 org.springframework.boot.builder.ParentContextCloserApplicationListener
这个监听器暂时还没找到具体的应用场景,但从源码注释和代码分析如下:
父容器对对子容器是可见的,但子容器对父容器是不可见的。那么如果父容器关闭了,但父容器又不知道自己的子容器,怎么取关闭子容器呢?
这就是这个监听器的作用。
在父容器可用的时候,在父容器中加入ContextCloserListener
监听器。这个监听器监听父容器的关闭事件,并且持有子容器的弱引用,关闭子容器。
public class ParentContextCloserApplicationListener
implements ApplicationListener<ParentContextAvailableEvent>, ApplicationContextAware, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE - 10;
private ApplicationContext context;
@Override
public int getOrder() {
return this.order;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
@Override
public void onApplicationEvent(ParentContextAvailableEvent event) {
maybeInstallListenerInParent(event.getApplicationContext());
}
private void maybeInstallListenerInParent(ConfigurableApplicationContext child) {
if (child == this.context && child.getParent() instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child.getParent();
parent.addApplicationListener(createContextCloserListener(child));
}
}
/**
* Subclasses may override to create their own subclass of ContextCloserListener. This
* still enforces the use of a weak reference.
* @param child the child context
* @return the {@link ContextCloserListener} to use
*/
protected ContextCloserListener createContextCloserListener(ConfigurableApplicationContext child) {
return new ContextCloserListener(child);
}
/**
* {@link ApplicationListener} to close the context.
*/
protected static class ContextCloserListener implements ApplicationListener<ContextClosedEvent> {
private WeakReference<ConfigurableApplicationContext> childContext;
public ContextCloserListener(ConfigurableApplicationContext childContext) {
this.childContext = new WeakReference<>(childContext);
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
ConfigurableApplicationContext context = this.childContext.get();
if ((context != null) && (event.getApplicationContext() == context.getParent()) && context.isActive()) {
context.close();
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof ContextCloserListener) {
ContextCloserListener other = (ContextCloserListener) obj;
return ObjectUtils.nullSafeEquals(this.childContext.get(), other.childContext.get());
}
return super.equals(obj);
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.childContext.get());
}
}
2.3 org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
这是针对CloudFoundryVcap环境变量的后置处理器,暂时没用到过,跳过。
2.4 org.springframework.boot.context.FileEncodingApplicationListener
监听ApplicationEnvironmentPreparedEvent
事件,在ConfigurableEnvironment
环境准备好后触发。检查spring环境配置的文件编码属性spring.mandatory-file-encoding
和jvm系统属性指定的文件编码file.encoding
是否一致。不一致则启动报错。
public class FileEncodingApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
private static final Log logger = LogFactory.getLog(FileEncodingApplicationListener.class);
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
String desired = environment.getProperty("spring.mandatory-file-encoding");
if (desired == null) {
return;
}
String encoding = System.getProperty("file.encoding");
if (encoding != null && !desired.equalsIgnoreCase(encoding)) {
if (logger.isErrorEnabled()) {
logger.error("System property 'file.encoding' is currently '" + encoding + "'. It should be '" + desired
+ "' (as defined in 'spring.mandatoryFileEncoding').");
logger.error("Environment variable LANG is '" + System.getenv("LANG")
+ "'. You could use a locale setting that matches encoding='" + desired + "'.");
logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL")
+ "'. You could use a locale setting that matches encoding='" + desired + "'.");
}
throw new IllegalStateException("The Java Virtual Machine has not been configured to use the "
+ "desired default character encoding (" + desired + ").");
}
}
}
2.5 org.springframework.boot.context.config.AnsiOutputApplicationListener
监听ApplicationEnvironmentPreparedEvent
事件,在环境准备好后,设置spring.output.ansi
的相关属性,可以用来控制是否彩色输出。
public class AnsiOutputApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
.ifBound(AnsiOutput::setEnabled);
AnsiOutput.setConsoleAvailable(environment.getProperty("spring.output.ansi.console-available", Boolean.class));
}
@Override
public int getOrder() {
// Apply after ConfigFileApplicationListener has called EnvironmentPostProcessors
return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
}
}
2.6 org.springframework.boot.context.config.ConfigFileApplicationListener
重要
实现了EnvironmentPostProcessor
接口,同时监听ApplicationEvent
事件。如下如,ApplicationEvent
事件监听,实际只处理ApplicationEnvironmentPreparedEvent
(环境准备完成)和ApplicationPreparedEvent
(spring容器准备好,但还没有刷新)事件。
2.6.1 ApplicationEnvironmentPreparedEvent
事件处理 onApplicationEnvironmentPreparedEvent()
会先加载spring.factories的org.springframework.boot.env.EnvironmentPostProcessor的配置,并实例化相关对象。然后把this加进去,因为当前类ConfigFileApplicationListener
也实现了EnvironmentPostProcessor
接口。所以一共有5个环境变量后置处理器进行后置处理。
5个后置处理器的顺序如下:
2.6.2. ApplicationPreparedEvent
事件处理onApplicationPreparedEvent(ApplicationEvent event)
通过分析一下源码,实际是往Spring容器中加入的PropertySourceOrderingPostProcessor
后置处理器。
2.6.3 实现了EnvironmentPostProcessor
接口,作为环境后置处理器的作用,后续介绍。
2.7 org.springframework.boot.context.config.DelegatingApplicationListener
这个监听器的作用的是将监听的‘ApplicationEvent’事件委托给指定的监听器进行处理。其实对于一个启动过程中的事件,我们可以直接在spring.factories文件进行配置相关的处理类。如果我们不想在spring.factories文件进行配置,那么可以利用这个监听器。
DelegatingApplicationListener
功能:监听context.listener.classes
属性下配置的监听器。
public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
// NOTE: Similar to org.springframework.web.context.ContextLoader
private static final String PROPERTY_NAME = "context.listener.classes";
private int order = 0;
private SimpleApplicationEventMulticaster multicaster;
@Override
public void onApplicationEvent(ApplicationEvent event) {
//如果Application环境已经准备好了,进行一下处理
if (event instanceof ApplicationEnvironmentPreparedEvent) {
//寻找并实例化"context.listener.classes"下配置的监听器
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
if (delegates.isEmpty()) {
return;
}
//新建一个简单的多路事件广播器
this.multicaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<ApplicationEvent> listener : delegates) {
this.multicaster.addApplicationListener(listener);
}
}
//如果有配置监听器的话,广播处理。
if (this.multicaster != null) {
this.multicaster.multicastEvent(event);
}
}
@SuppressWarnings("unchecked")
private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) {
if (environment == null) {
return Collections.emptyList();
}
String classNames = environment.getProperty(PROPERTY_NAME);
List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
try {
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationListener.class, clazz,
"class [" + className + "] must implement ApplicationListener");
listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils.instantiateClass(clazz));
}
catch (Exception ex) {
throw new ApplicationContextException("Failed to load context listener class [" + className + "]",
ex);
}
}
}
AnnotationAwareOrderComparator.sort(listeners);
return listeners;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
2.8 org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
这个监听器感觉没什么作用。就是在bebug级别下,在ApplicationEnvironmentPreparedEvent
和ApplicationFailedEvent
,即环境准备好和启动失败下,打印日志,输出classpath下的所有jar包。
2.9 org.springframework.boot.context.logging.LoggingApplicationListener
这个监听器的作用是在启动过程中,根据配置文件生产LoggingSystem
实例。LoggingSystem
,LoggingSystem是SpringBoot对日志系统的抽象,是一个顶层的抽象类,我们先来看下它的整体结构:详情见《SpringBoot LoggingSystem相关源码分析》
2.9.1 监听的事件和事件源
LoggingApplicationListener
实现了GenericApplicationListener
接口,所以是根据以下两个方法来确定支持的事件和事件源的。
//监听的事件:ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ContextClosedEvent、ApplicationFailedEvent
private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class,
ApplicationFailedEvent.class };
//监听的事件源:SpringApplication、ApplicationContext
private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class, ApplicationContext.class };
public boolean supportsEventType(ResolvableType resolvableType) {
return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
}
public boolean supportsSourceType(Class<?> sourceType) {
return isAssignableFrom(sourceType, SOURCE_TYPES);
}
2.9.2 onApplicationEvent(ApplicationEvent event)
方法详解
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
1) ApplicationStartingEvent
事件,容器开始启动
1. 自动在classpath目录找到相应的slf4j的实现类,然后生成对应的loggingSystem。默认是logback;
2. 进行初始化之前的一些工作(详情可以自己去看下源码)
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
this.loggingSystem.beforeInitialize();
}
2) onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event)
环境准备好
环境准备好了,那么我们已经在spring的配置文件中读取到了日志的相关配置,这个时候,我们可以进行日志类的初始化了。
主要是loggingSystem 对象配置日志级别、输出文件等
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
if (this.loggingSystem == null) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
}
initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
new LoggingSystemProperties(environment).apply();
this.logFile = LogFile.get(environment);
if (this.logFile != null) {
this.logFile.applyToSystemProperties();
}
this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
initializeEarlyLoggingLevel(environment);
initializeSystem(environment, this.loggingSystem, this.logFile);
initializeFinalLoggingLevels(environment, this.loggingSystem);
registerShutdownHookIfNecessary(environment, this.loggingSystem);
}
3) onApplicationPreparedEvent(ApplicationPreparedEvent event)
spring容器准备好
spring启动好之后,将loggingSystem、logFile、loggerGroups注册到容器中。
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
}
if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
}
if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
}
}
4) ContextClosedEvent
和ApplicationFailedEvent
调用cleanUp()方法
private void onContextClosedEvent() {
if (this.loggingSystem != null) {
this.loggingSystem.cleanUp();
}
}
private void onApplicationFailedEvent() {
if (this.loggingSystem != null) {
this.loggingSystem.cleanUp();
}
}
2.10 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
不知道这是干什么,据说是初始化 Liquibase 的 ServiceLocator 对象,是一个用于工作版本替换的工具。反正我看不懂。
public class LiquibaseServiceLocatorApplicationListener implements ApplicationListener<ApplicationStartingEvent> {
private static final Log logger = LogFactory.getLog(LiquibaseServiceLocatorApplicationListener.class);
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
if (ClassUtils.isPresent("liquibase.servicelocator.CustomResolverServiceLocator",
event.getSpringApplication().getClassLoader())) {
new LiquibasePresent().replaceServiceLocator();
}
}
/**
* Inner class to prevent class not found issues.
*/
private static class LiquibasePresent {
void replaceServiceLocator() {
CustomResolverServiceLocator customResolverServiceLocator = new CustomResolverServiceLocator(
new SpringPackageScanClassResolver(logger));
ServiceLocator.setInstance(customResolverServiceLocator);
}
}
}
2.11 org.springframework.boot.autoconfigure.BackgroundPreinitializer
这个监听器的作用是让一些耗时的任务提前在后台进程(子线程)触发初始化。默认是开启的,可以通过系统参数spring.backgroundpreinitializer.ignore
为true来跳过。
这个类虽然很短,但个人觉得里面有三个地方设计的很好,
2.11.1 利用AtomicBoolean.compareAndSet()
实现后台进程只会执行一次
SpringApplicationEvent
事件会被发布多次,但是我们的后台进程初始化任务实际只需要调用一次,并且需要解决多线程并发的问题。这里采用的是原子类+CAS来实现多线程的安全。(这个可以用到单例模式中,解决多线程的问题)
2.11.2 利用CountDownLatch
保证后台进程的初始化任务一定完成
如果启动springboot的前台进程进程已经完成了,并且发布了ApplicationReadyEvent
事件,可以对外提供服务,但这时候后台初始化的任务还没有完成,那就尴尬了,这时候实际上还没有办法正常对外提供服务。private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);
,只有子线程进行了preinitializationComplete.countDown();
,主线程才会继续执行,不然主线程会在发布ApplicationReadyEvent
事件时阻塞(preinitializationComplete.await();
)
2.11.3 有哪些任务需要后台进行初始化?
MessageConverterInitializer
:MessageConverters
提前初始化;ValidationInitializer
:javax.validation
提前初始化;JacksonInitializer
:Jackson
提前初始化;ConversionServiceInitializer
:ConversionService
提前初始化;
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {
/**
* System property that instructs Spring Boot how to run pre initialization. When the
* property is set to {@code true}, no pre-initialization happens and each item is
* initialized in the foreground as it needs to. When the property is {@code false}
* (default), pre initialization runs in a separate thread in the background.
* @since 2.1.0
*/
public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";
private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(false);
private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);
@Override
public void onApplicationEvent(SpringApplicationEvent event) {
if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
&& event instanceof ApplicationEnvironmentPreparedEvent && multipleProcessors()
&& preinitializationStarted.compareAndSet(false, true)) {
performPreinitialization();
}
if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
&& preinitializationStarted.get()) {
try {
preinitializationComplete.await();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
private boolean multipleProcessors() {
return Runtime.getRuntime().availableProcessors() > 1;
}
private void performPreinitialization() {
try {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
runSafely(new ConversionServiceInitializer());
runSafely(new ValidationInitializer());
runSafely(new MessageConverterInitializer());
runSafely(new JacksonInitializer());
runSafely(new CharsetInitializer());
preinitializationComplete.countDown();
}
public void runSafely(Runnable runnable) {
try {
runnable.run();
}
catch (Throwable ex) {
// Ignore
}
}
}, "background-preinit");
thread.start();
}
catch (Exception ex) {
// This will fail on GAE where creating threads is prohibited. We can safely
// continue but startup will be slightly slower as the initialization will now
// happen on the main thread.
preinitializationComplete.countDown();
}
}
/**
* Early initializer for Spring MessageConverters.
*/
private static class MessageConverterInitializer implements Runnable {
@Override
public void run() {
new AllEncompassingFormHttpMessageConverter();
}
}
/**
* Early initializer for javax.validation.
*/
private static class ValidationInitializer implements Runnable {
@Override
public void run() {
Configuration<?> configuration = Validation.byDefaultProvider().configure();
configuration.buildValidatorFactory().getValidator();
}
}
/**
* Early initializer for Jackson.
*/
private static class JacksonInitializer implements Runnable {
@Override
public void run() {
Jackson2ObjectMapperBuilder.json().build();
}
}
/**
* Early initializer for Spring's ConversionService.
*/
private static class ConversionServiceInitializer implements Runnable {
@Override
public void run() {
new DefaultFormattingConversionService();
}
}
private static class CharsetInitializer implements Runnable {
@Override
public void run() {
StandardCharsets.UTF_8.name();
}
}
}