springboot源码解析(3)启动过程的11个监听器

这里写目录标题

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下的文件ApplicationEnvironmentPreparedEventApplicationFailedEvent
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级别下,在ApplicationEnvironmentPreparedEventApplicationFailedEvent,即环境准备好和启动失败下,打印日志,输出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) ContextClosedEventApplicationFailedEvent

调用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 有哪些任务需要后台进行初始化?
  1. MessageConverterInitializerMessageConverters提前初始化;
  2. ValidationInitializerjavax.validation提前初始化;
  3. JacksonInitializerJackson提前初始化;
  4. ConversionServiceInitializerConversionService提前初始化;
@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();
		}

	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值