简单读!springboot 启动过程概览
springboot 说是没有新技术,但是刚开始用的时候,总有许多疑问,它是怎么做到的?让我们通过源码来解释吧!
让我们来看一个springboot项目的启动过程吧!
启动类,即一个main入口:
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableTransactionManagement @MapperScan("com.xxx.dao") @EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class}) @ComponentScan(basePackages = "com.xxx.**,") public class StartApplication { public static void main(String[] args) { SpringApplication.run(StartApplication.class, args); } }
运行该方法后,springboot服务就起来了!
其作用就是,通过注解加入一些配置,然后交由SpringApplication.run()进行操作,因此,可以认为,springboot的启动方法为 SpringApplication.run();
/** * Static helper that can be used to run a {@link SpringApplication} from the * specified source using default settings. * @param primarySource the primary source to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } /** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param primarySources the primary sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
SpringApplication.run()做了两件事:
1. new一个 SpringApplication 实例,将启动类信息传递进去;
2. 调用SpringApplication的实例方法run(),注意,这个run与前面的run() 是不一样的,前端的run()是静态方法,是为了方便外部调用,而实例的run()才是做实事的!
我们先看实例化 SpringApplication 怎么样的?
// org.springframework.boot.SpringApplication public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } /** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param resourceLoader the resource loader to use * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #setSources(Set) */ @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = deduceWebApplicationType(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
想来该实例化过程应该不会太简单,毕竟整个应用的启动都需要交给它来承担了。其主要做这几件事:
1. 将启动类设值到实例变量中,以备其他用途;
2. 推断出当前启动的应用类型,有:WebApplicationType.REACTIVE/NONE/SERVLET 三种模式,用于后续进行创建应用引擎;
3. 设置 ApplicationContextInitializer.class 的初始化相关类;
4. 设置 ApplicationListener.class 的相关监听类;
5. 推断出入口类,并实例化一个入口类,即: StartApplication;(注意启动的时候入口类并没有进行实例化)
其中,获取的 initializer / listeners 主要有:
Initializers: - org.springframework.boot.context.config.DelegatingApplicationContextInitializer - org.springframework.boot.context.ContextIdApplicationContextInitializer - org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer - org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer - org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer - org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener Listeners: - org.springframework.cloud.bootstrap.BootstrapApplicationListener - org.springframework.cloud.bootstrap.LoggingSystemShutdownListener - org.springframework.boot.context.config.ConfigFileApplicationListener - org.springframework.boot.context.config.AnsiOutputApplicationListener - org.springframework.boot.context.logging.LoggingApplicationListener - org.springframework.boot.context.logging.ClasspathLoggingApplicationListener - org.springframework.boot.autoconfigure.BackgroundPreinitializer - org.springframework.boot.context.config.DelegatingApplicationListener - org.springframework.cloud.context.restart.RestartListener - org.springframework.boot.builder.ParentContextCloserApplicationListener - org.springframework.boot.ClearCachesApplicationListener - org.springframework.boot.context.FileEncodingApplicationListener - org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener BootstrapConfiguration.class - org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration - org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration - org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration - org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration - org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration - org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration
其中一些启动监听时的栈桢如下:
"main@1" prio=5 tid=0x1 nid=NA runnable java.lang.Thread.State: RUNNABLE at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:207) at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:104) at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:70) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74) at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54) at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:364) at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265) at com.xxx.service.StartApplication.main(StartApplication.java:26)
BootstrapApplicationListener 则会对整个应用进程进行启动,它会再次回来调用 SpringApplication, 进行应用刷新动作:
"main@1" prio=5 tid=0x1 nid=NA runnable java.lang.Thread.State: RUNNABLE at org.springframework.boot.builder.SpringApplicationBuilder.createSpringApplication(SpringApplicationBuilder.java:102) at org.springframework.boot.builder.SpringApplicationBuilder.<init>(SpringApplicationBuilder.java:90) at org.springframework.cloud.stream.binder.DefaultBinderFactory.getBinderInstance(DefaultBinderFactory.java:187) at org.springframework.cloud.stream.binder.DefaultBinderFactory.getBinder(DefaultBinderFactory.java:139) - locked <0x34b5> (a org.springframework.cloud.stream.binder.DefaultBinderFactory) at org.springframework.cloud.stream.binding.BindingService.getBinder(BindingService.java:308) at org.springframework.cloud.stream.binding.BindingService.bindProducer(BindingService.java:208) at org.springframework.cloud.stream.binding.BindableProxyFactory.bindOutputs(BindableProxyFactory.java:252) at org.springframework.cloud.stream.binding.OutputBindingLifecycle.doStartWithBindable(OutputBindingLifecycle.java:46) at org.springframework.cloud.stream.binding.AbstractBindingLifecycle$$Lambda$726.576149775.accept(Unknown Source:-1) at java.util.Iterator.forEachRemaining(Iterator.java:116) at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) at org.springframework.cloud.stream.binding.AbstractBindingLifecycle.start(AbstractBindingLifecycle.java:47) at org.springframework.cloud.stream.binding.OutputBindingLifecycle.start(OutputBindingLifecycle.java:29) at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182) at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53) at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360) at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158) at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:885) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) - locked <0x34df> (a java.lang.Object) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265) at com.xxx.service.StartApplication.main(StartApplication.java:26)
而对于经典的spring架构,都会进行一个 refresh() 的操作,在spring中则体现为:
# refresh "main@1" prio=5 tid=0x1 nid=NA runnable java.lang.Thread.State: RUNNABLE at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:891) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) - locked <0x34df> (a java.lang.Object) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265) at com.xxx.service.StartApplication.main(StartApplication.java:26)
SpringApplication.run() 方法,其体现的是整体架构过程:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
步骤已经很清晰:
1. 配置 headless 属性信息;
2. 开启 listeners.starting(),为后续事件做准备;
3. 环境准备,bootstrap,application...; printBanner()的作用是打印[SpringBoot]字样的logo作用;
4. 创建 ConfigurableApplicationContext 应用上下文;
5. 准备上下文需要的信息,设值,hook...
6. 刷新上下文 refreshContext(); 重点初始化在此;
7. 刷新后续钩子处理;
8. 监听器监听started方法 listeners.started();
9. callRunners 回调;
10. 监听器监听 running方法, listeners.running();
11. 其他,整个项目在运行期间,大量利用监听器 listeners 的监听,进行注册各自的服务;
我们主要来看一下springboot的事件监听和响应过程,把这个过程明白了,整个springboot也就清晰了:
// org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(), 监听生命周期事件 @Override public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); } @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } /** * Invoke the given listener with the given event. * @param listener the ApplicationListener to invoke * @param event the current event to propagate * @since 4.1 */ protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { // call doInvokeListener(listener, event); } } // doInvokeListener @SuppressWarnings({"unchecked", "rawtypes"}) private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isDebugEnabled()) { logger.debug("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
可以看到,其实事件监听入口只有一个,不过到下面的时候,都会重新去获取一次监听者列表,进行各自通知,从而独立加载各组件!
监听器可能有如下几个:
- org.springframework.cloud.bootstrap.BootstrapApplicationListener - org.springframework.cloud.bootstrap.LoggingSystemShutdownListener - org.springframework.boot.context.config.ConfigFileApplicationListener - org.springframework.boot.context.config.AnsiOutputApplicationListener - org.springframework.boot.context.logging.LoggingApplicationListener - org.springframework.boot.context.logging.ClasspathLoggingApplicationListener - org.springframework.boot.autoconfigure.BackgroundPreinitializer - org.springframework.boot.context.config.DelegatingApplicationListener - org.springframework.boot.context.FileEncodingApplicationListener
其中,重要的监听器: BootstrapApplicationListener, 会触发应用的启动动作;其监听 onApplicationEvent() 如下:
@Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) { return; } // don't listen to events in a bootstrap context if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { return; } ConfigurableApplicationContext context = null; String configName = environment .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); for (ApplicationContextInitializer<?> initializer : event.getSpringApplication() .getInitializers()) { if (initializer instanceof ParentContextApplicationContextInitializer) { context = findBootstrapContext( (ParentContextApplicationContextInitializer) initializer, configName); } } if (context == null) { // 如果是第一次访问,就会走初始化动作 context = bootstrapServiceContext(environment, event.getSpringApplication(), configName); } apply(context, event.getSpringApplication(), environment); }
如上的 BootstrapApplicationListener.onApplicationEvent() 主要做的事:
1. 检测是否启用配置参数 spring.cloud.bootstrap.enabled, 是否有 bootstrap 的资源(如果有的话意味着当前正在或已经加载完成bootstrp),从而决定是否监听事件;
2. 根据 ${spring.cloud.bootstrap.name:bootstrap} 配置,查找 bootstrapContext;
3. 如果没有找到 bootstrapContext; 则进行初始化;
4. 将context应用到apply() 中;
// 初始化 bootstrap private ConfigurableApplicationContext bootstrapServiceContext( ConfigurableEnvironment environment, final SpringApplication application, String configName) { StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); MutablePropertySources bootstrapProperties = bootstrapEnvironment .getPropertySources(); for (PropertySource<?> source : bootstrapProperties) { bootstrapProperties.remove(source.getName()); } String configLocation = environment .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); Map<String, Object> bootstrapMap = new HashMap<>(); bootstrapMap.put("spring.config.name", configName); // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail // force the environment to use none, because if though it is set below in the builder // the environment overrides it bootstrapMap.put("spring.main.web-application-type", "none"); if (StringUtils.hasText(configLocation)) { bootstrapMap.put("spring.config.location", configLocation); } bootstrapProperties.addFirst( new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for (PropertySource<?> source : environment.getPropertySources()) { if (source instanceof StubPropertySource) { continue; } bootstrapProperties.addLast(source); } ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates List<String> names = new ArrayList<>(SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader)); for (String name : StringUtils.commaDelimitedListToStringArray( environment.getProperty("spring.cloud.bootstrap.sources", ""))) { names.add(name); } // TODO: is it possible or sensible to share a ResourceLoader? SpringApplicationBuilder builder = new SpringApplicationBuilder() .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) .environment(bootstrapEnvironment) // Don't use the default properties in this builder .registerShutdownHook(false).logStartupInfo(false) .web(WebApplicationType.NONE); if (environment.getPropertySources().contains("refreshArgs")) { // If we are doing a context refresh, really we only want to refresh the // Environment, and there are some toxic listeners (like the // LoggingApplicationListener) that affect global static state, so we need a // way to switch those off. builder.application() .setListeners(filterListeners(builder.application().getListeners())); } List<Class<?>> sources = new ArrayList<>(); for (String name : names) { Class<?> cls = ClassUtils.resolveClassName(name, null); try { cls.getDeclaredAnnotations(); } catch (Exception e) { continue; } sources.add(cls); } AnnotationAwareOrderComparator.sort(sources); builder.sources(sources.toArray(new Class[sources.size()])); // 创建 context, 使用 SpringApplicationBuilder 创建, 最终调用到 SpringApplication.run(), 从而形成一个递归 final ConfigurableApplicationContext context = builder.run(); // gh-214 using spring.application.name=bootstrap to set the context id via // `ContextIdApplicationContextInitializer` prevents apps from getting the actual // spring.application.name // during the bootstrap phase. context.setId("bootstrap"); // Make the bootstrap context a parent of the app context addAncestorInitializer(application, context); // It only has properties in it now that we don't want in the parent so remove // it (and it will be added back later) bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); return context; }
第二个listener: LoggingSystemShutdownListener, 其作用是注册日志组件,为context打印日志提供能力;其 onApplicationEvent() 响应如下:
//org.springframework.cloud.bootstrap.LoggingSystemShutdownListener @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { shutdownLogging(); } private void shutdownLogging() { LoggingSystem loggingSystem = LoggingSystem .get(ClassUtils.getDefaultClassLoader()); loggingSystem.cleanUp(); loggingSystem.beforeInitialize(); }
第三个 listener 是 ConfigFileApplicationListener, 在这里处理多配置文件的问题,其 onApplicationEvent() 监听如下:
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } }
针对 ApplicationEnvironmentPreparedEvent 环境准备事件,触发初始化动作;
private void onApplicationEnvironmentPreparedEvent( ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } }
使用的处理器有:
- org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor - org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor - org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor - org.springframework.cloud.client.HostInfoEnvironmentPostProcessor - org.springframework.boot.context.config.ConfigFileApplicationListener - org.springframework.cloud.bus.BusEnvironmentPostProcessor
针对 SystemEnvironmentPropertySourceEnvironmentPostProcessor 会去处理 bootstrap 的引入问题,载入系统的配置,如JAVA_HOME:
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME; PropertySource<?> propertySource = environment.getPropertySources() .get(sourceName); if (propertySource != null) { replacePropertySource(environment, sourceName, propertySource); } } @SuppressWarnings("unchecked") private void replacePropertySource(ConfigurableEnvironment environment, String sourceName, PropertySource<?> propertySource) { Map<String, Object> originalSource = (Map<String, Object>) propertySource .getSource(); SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource( sourceName, originalSource); environment.getPropertySources().replace(sourceName, source); }
接下来是对 SpringApplicationJsonEnvironmentPostProcessor 的处理过程:
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { MutablePropertySources propertySources = environment.getPropertySources(); StreamSupport.stream(propertySources.spliterator(), false) .map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst() .ifPresent((v) -> processJson(environment, v)); } private void processJson(ConfigurableEnvironment environment, JsonPropertyValue propertyValue) { try { JsonParser parser = JsonParserFactory.getJsonParser(); Map<String, Object> map = parser.parseMap(propertyValue.getJson()); if (!map.isEmpty()) { addJsonPropertySource(environment, new JsonPropertySource(propertyValue, flatten(map))); } } catch (Exception ex) { logger.warn("Cannot parse JSON for spring.application.json: " + propertyValue.getJson(), ex); } }
接下来是对 CloudFoundryVcapEnvironmentPostProcessor 处事件监听处理,主要是对云上配置的设置,如果不是springCloud,则直接忽略:
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) { Properties properties = new Properties(); addWithPrefix(properties, getPropertiesFromApplication(environment), "vcap.application."); addWithPrefix(properties, getPropertiesFromServices(environment), "vcap.services."); MutablePropertySources propertySources = environment.getPropertySources(); if (propertySources.contains( CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) { propertySources.addAfter( CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME, new PropertiesPropertySource("vcap", properties)); } else { propertySources .addFirst(new PropertiesPropertySource("vcap", properties)); } } }
接下来是对 HostInfoEnvironmentPostProcessor, 域名的解析,也主要针对cloud环境的验证:
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { InetUtils.HostInfo hostInfo = getFirstNonLoopbackHostInfo(environment); LinkedHashMap<String, Object> map = new LinkedHashMap<>(); map.put("spring.cloud.client.hostname", hostInfo.getHostname()); map.put("spring.cloud.client.ip-address", hostInfo.getIpAddress()); MapPropertySource propertySource = new MapPropertySource( "springCloudClientHostInfo", map); environment.getPropertySources().addLast(propertySource); }
接下来是对 ConfigFileApplicationListener 的监听:
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { addPropertySources(environment, application.getResourceLoader()); } // Loader.Loader() Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { this.environment = environment; this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(); // 这里资源加载方式有两种 // - org.springframework.boot.env.PropertiesPropertySourceLoader // - org.springframework.boot.env.YamlPropertySourceLoader this.propertySourceLoaders = SpringFactoriesLoader.loadFactories( PropertySourceLoader.class, getClass().getClassLoader()); } // 装载具体的配置文件 public void load() { this.profiles = new LinkedList<>(); this.processedProfiles = new LinkedList<>(); this.activatedProfiles = false; this.loaded = new LinkedHashMap<>(); initializeProfiles(); // 对 default, dev ... , 进行遍历, 即在此处载入环境配置文件 while (!this.profiles.isEmpty()) { Profile profile = this.profiles.poll(); if (profile != null && !profile.isDefaultProfile()) { addProfileToEnvironment(profile.getName()); } load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } // 重置环境配置 resetEnvironmentProfiles(this.processedProfiles); // 载入配置变量,加载 load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); addLoadedPropertySources(); } /** * Initialize profile information from both the {@link Environment} active * profiles and any {@code spring.profiles.active}/{@code spring.profiles.include} * properties that are already set. */ private void initializeProfiles() { // The default profile for these purposes is represented as null. We add it // first so that it is processed first and has lowest priority. this.profiles.add(null); // 这里获取到主空 Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty(); // 获取其他配置文件 this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty)); // Any pre-existing active profiles set via property sources (e.g. // System properties) take precedence over those added in config files. addActiveProfiles(activatedViaProperty); if (this.profiles.size() == 1) { // only has null profile for (String defaultProfileName : this.environment.getDefaultProfiles()) { Profile defaultProfile = new Profile(defaultProfileName, true); this.profiles.add(defaultProfile); } } } // 搜索配置文件,注意配置文件是搜索得来的 private Set<String> getSearchLocations() { // spring.config.location, 如果配置的指定目录,则从那里搜索 if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) { return getSearchLocations(CONFIG_LOCATION_PROPERTY); } // 否则加载可能的目录 // - classpath:/ // - classpath:/config/ // - file:./ // - file:./config/ Set<String> locations = getSearchLocations( CONFIG_ADDITIONAL_LOCATION_PROPERTY); locations.addAll( asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS)); return locations; }
接下来是 AnsiOutputApplicationListener 字符编码监听;
@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)); }
日志监听 LoggingApplicationListener, :
@Override 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(); } } // 环境准备事件 private void onApplicationEnvironmentPreparedEvent( ApplicationEnvironmentPreparedEvent event) { if (this.loggingSystem == null) { this.loggingSystem = LoggingSystem .get(event.getSpringApplication().getClassLoader()); } initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader()); } /** * Initialize the logging system according to preferences expressed through the * {@link Environment} and the classpath. * @param environment the environment * @param classLoader the classloader */ protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { new LoggingSystemProperties(environment).apply(); LogFile logFile = LogFile.get(environment); if (logFile != null) { logFile.applyToSystemProperties(); } initializeEarlyLoggingLevel(environment); initializeSystem(environment, this.loggingSystem, logFile); // 第一次初始化日志系统 initializeFinalLoggingLevels(environment, this.loggingSystem); registerShutdownHookIfNecessary(environment, this.loggingSystem); }
接下来是 ClasspathLoggingApplicationListener 的环境准备监听,为空实现,只打印日志:
@Override public void onApplicationEvent(ApplicationEvent event) { if (logger.isDebugEnabled()) { if (event instanceof ApplicationEnvironmentPreparedEvent) { logger.debug("Application started with classpath: " + getClasspath()); } else if (event instanceof ApplicationFailedEvent) { logger.debug( "Application failed to start with classpath: " + getClasspath()); } } }
接下来是 BackgroundPreinitializer, 后台管理监听,其不监听环境准备事件:
@Override public void onApplicationEvent(SpringApplicationEvent event) { if (event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) { performPreinitialization(); } if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) && preinitializationStarted.get()) { try { preinitializationComplete.await(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } }
接下来是 DelegatingApplicationListener, 使其他自定义监听可以监听:
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { 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); } }
接下来是 FileEncodingApplicationListener,
@Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.containsProperty("spring.mandatory-file-encoding")) { return; } String encoding = System.getProperty("file.encoding"); String desired = environment.getProperty("spring.mandatory-file-encoding"); if (encoding != null && !desired.equalsIgnoreCase(encoding)) { 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 + ")."); } } /** * Apply any {@link ApplicationContextInitializer}s to the context before it is * refreshed. * @param context the configured ApplicationContext (not refreshed yet) * @see ConfigurableApplicationContext#refresh() */ @SuppressWarnings({ "rawtypes", "unchecked" }) protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
初始化器列表:
- org.springframework.boot.context.config.DelegatingApplicationContextInitializer - org.springframework.boot.context.ContextIdApplicationContextInitializer - org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer - org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer - org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer - org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
autoConfigurationReport 单例加载,加载资源文件,加载开始!
/** * Load beans into the application context. * @param context the context to load beans into * @param sources the sources to load */ protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug( "Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader( getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); } // contextLoaded() 事件监听 public void contextLoaded(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.contextLoaded(context); } } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } // 加载 bootstrap.properties, bootstrap-dev.properties... // 在 ConfigFileApplicationListener 的 ApplicationPreparedEvent 事件中触发 this.initialMulticaster.multicastEvent( new ApplicationPreparedEvent(this.application, this.args, context)); } // 如: private void onApplicationPreparedEvent(ApplicationEvent event) { this.logger.replayTo(ConfigFileApplicationListener.class); addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext()); }
重点关注, 即是进入原本的 SpringApplicationContext 容器中进行 各种bean 初始化了:
// refreshContext(context); 重点关注, 即是进入原本的 SpringApplicationContext 容器中进行 各种bean 初始化了 private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } // 熟悉的模式 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
而在上下文刷新时,contextRefresh() 会触发这些 listeners:
- org.springframework.boot.context.config.DelegatingApplicationListener - org.springframework.cloud.context.restart.RestartListener - org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener$ConditionEvaluationReportListener - org.springframework.boot.ClearCachesApplicationListener - org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$SharedMetadataReaderFactoryBean
同样,DelegatingApplicationListener 是给其他监听器一个执行的时机的机会!
RestartListener 则是负责响应应用的启动停止事件:
// RestartListener @Override public void onApplicationEvent(ApplicationEvent input) { if (input instanceof ApplicationPreparedEvent) { this.event = (ApplicationPreparedEvent) input; if (this.context == null) { this.context = this.event.getApplicationContext(); } } else if (input instanceof ContextRefreshedEvent) { if (this.context != null && input.getSource().equals(this.context) && this.event != null) { this.context.publishEvent(this.event); } } else { if (this.context != null && input.getSource().equals(this.context)) { this.context = null; this.event = null; } }
接下来是 ConditionEvaluationReportLoggingListener, 他会打印报告;
@Override public void onApplicationEvent(ApplicationEvent event) { ConditionEvaluationReportLoggingListener.this.onApplicationEvent(event); } protected void onApplicationEvent(ApplicationEvent event) { ConfigurableApplicationContext initializerApplicationContext = this.applicationContext; if (event instanceof ContextRefreshedEvent) { if (((ApplicationContextEvent) event) .getApplicationContext() == initializerApplicationContext) { logAutoConfigurationReport(); } } else if (event instanceof ApplicationFailedEvent && ((ApplicationFailedEvent) event) .getApplicationContext() == initializerApplicationContext) { logAutoConfigurationReport(true); } }
让我们回到 bootstrap 加载开始的地方,BootstrapApplication 加载完成后,合并属性;
private ConfigurableApplicationContext bootstrapServiceContext( ConfigurableEnvironment environment, final SpringApplication application, String configName) { StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); MutablePropertySources bootstrapProperties = bootstrapEnvironment .getPropertySources(); for (PropertySource<?> source : bootstrapProperties) { bootstrapProperties.remove(source.getName()); } String configLocation = environment .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); Map<String, Object> bootstrapMap = new HashMap<>(); bootstrapMap.put("spring.config.name", configName); // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail // force the environment to use none, because if though it is set below in the builder // the environment overrides it bootstrapMap.put("spring.main.web-application-type", "none"); if (StringUtils.hasText(configLocation)) { bootstrapMap.put("spring.config.location", configLocation); } bootstrapProperties.addFirst( new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for (PropertySource<?> source : environment.getPropertySources()) { if (source instanceof StubPropertySource) { continue; } bootstrapProperties.addLast(source); } ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates List<String> names = new ArrayList<>(SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader)); for (String name : StringUtils.commaDelimitedListToStringArray( environment.getProperty("spring.cloud.bootstrap.sources", ""))) { names.add(name); } // TODO: is it possible or sensible to share a ResourceLoader? SpringApplicationBuilder builder = new SpringApplicationBuilder() .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) .environment(bootstrapEnvironment) // Don't use the default properties in this builder .registerShutdownHook(false).logStartupInfo(false) .web(WebApplicationType.NONE); if (environment.getPropertySources().contains("refreshArgs")) { // If we are doing a context refresh, really we only want to refresh the // Environment, and there are some toxic listeners (like the // LoggingApplicationListener) that affect global static state, so we need a // way to switch those off. builder.application() .setListeners(filterListeners(builder.application().getListeners())); } List<Class<?>> sources = new ArrayList<>(); for (String name : names) { Class<?> cls = ClassUtils.resolveClassName(name, null); try { cls.getDeclaredAnnotations(); } catch (Exception e) { continue; } sources.add(cls); } AnnotationAwareOrderComparator.sort(sources); builder.sources(sources.toArray(new Class[sources.size()])); final ConfigurableApplicationContext context = builder.run(); // gh-214 using spring.application.name=bootstrap to set the context id via // `ContextIdApplicationContextInitializer` prevents apps from getting the actual // spring.application.name // during the bootstrap phase. context.setId("bootstrap"); // Make the bootstrap context a parent of the app context addAncestorInitializer(application, context); // It only has properties in it now that we don't want in the parent so remove // it (and it will be added back later) bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); return context; }
其中涉及到的属性主要有:
- org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource - org.springframework.core.env.MapPropertySource - org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource - org.springframework.boot.env.RandomValuePropertySource - org.springframework.core.env.MapPropertySource - org.springframework.boot.env.OriginTrackedMapPropertySource - org.springframework.boot.env.OriginTrackedMapPropertySource - org.springframework.core.env.MapPropertySource
最后,再来看 BootstrapApplication 的 onApplicationEvent() 事件:
// BootstrapApplicationListener.onApplicationEvent() @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) { return; } // don't listen to events in a bootstrap context if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { return; } ConfigurableApplicationContext context = null; String configName = environment .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); for (ApplicationContextInitializer<?> initializer : event.getSpringApplication() .getInitializers()) { if (initializer instanceof ParentContextApplicationContextInitializer) { // 加载 BootstrapApplication context = findBootstrapContext( (ParentContextApplicationContextInitializer) initializer, configName); } } if (context == null) { context = bootstrapServiceContext(environment, event.getSpringApplication(), configName); } apply(context, event.getSpringApplication(), environment); } // 注册beans... private void apply(ConfigurableApplicationContext context, SpringApplication application, ConfigurableEnvironment environment) { @SuppressWarnings("rawtypes") List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context, ApplicationContextInitializer.class); application.addInitializers(initializers .toArray(new ApplicationContextInitializer[initializers.size()])); addBootstrapDecryptInitializer(application); } private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context, Class<T> type) { List<T> result = new ArrayList<T>(); // propertySourceBootstrapConfiguration, environmentDecryptApplicationListener for (String name : context.getBeanNamesForType(type)) { result.add(context.getBean(name, type)); } AnnotationAwareOrderComparator.sort(result); return result; }
最后,回到最初加载的地方: BootstrapApplicationListener, 下一个:
- org.springframework.cloud.bootstrap.BootstrapApplicationListener - org.springframework.cloud.bootstrap.LoggingSystemShutdownListener - org.springframework.boot.context.config.ConfigFileApplicationListener - org.springframework.boot.context.config.AnsiOutputApplicationListener - org.springframework.boot.context.logging.LoggingApplicationListener - org.springframework.boot.context.logging.ClasspathLoggingApplicationListener - org.springframework.boot.autoconfigure.BackgroundPreinitializer - org.springframework.boot.context.config.DelegatingApplicationListener - org.springframework.boot.context.FileEncodingApplicationListener
ClasspathLoggingApplicationListener, 加载 classpath 目录下的文件,即 jar 包系列:

Application started with classpath: [file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/charsets.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/deploy.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/access-bridge-64.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/cldrdata.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/dnsns.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/jaccess.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/jfxrt.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/localedata.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/nashorn.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunec.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunjce_provider.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunmscapi.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/sunpkcs11.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/ext/zipfs.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/javaws.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jce.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jfr.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jfxswt.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/jsse.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/management-agent.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/plugin.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/resources.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/jre/lib/rt.jar, file:/D:/www/java/richCash/target/classes/, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-amqp/2.0.5.RELEASE/spring-boot-starter-amqp-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter/2.0.5.RELEASE/spring-boot-starter-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot/2.0.5.RELEASE/spring-boot-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.0.5.RELEASE/spring-boot-autoconfigure-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.0.5.RELEASE/spring-boot-starter-logging-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.10.0/log4j-to-slf4j-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/logging/log4j/log4j-api/2.10.0/log4j-api-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar, file:/C:/Users/weiyong/.m2/repository/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/org/yaml/snakeyaml/1.19/snakeyaml-1.19.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-messaging/5.0.9.RELEASE/spring-messaging-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-beans/5.0.9.RELEASE/spring-beans-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/amqp/spring-rabbit/2.0.6.RELEASE/spring-rabbit-2.0.6.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/amqp/spring-amqp/2.0.6.RELEASE/spring-amqp-2.0.6.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/rabbitmq/amqp-client/5.4.1/amqp-client-5.4.1.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-tx/5.0.9.RELEASE/spring-tx-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-config/2.0.1.RELEASE/spring-cloud-starter-config-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter/2.0.1.RELEASE/spring-cloud-starter-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-context/2.0.1.RELEASE/spring-cloud-context-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/security/spring-security-rsa/1.0.5.RELEASE/spring-security-rsa-1.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/bouncycastle/bcpkix-jdk15on/1.56/bcpkix-jdk15on-1.56.jar, file:/C:/Users/weiyong/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.56/bcprov-jdk15on-1.56.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-config-client/2.0.1.RELEASE/spring-cloud-config-client-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.9.0/jackson-annotations-2.9.0.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.9.6/jackson-databind-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.9.6/jackson-core-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-aop/2.0.5.RELEASE/spring-boot-starter-aop-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-aop/5.0.9.RELEASE/spring-aop-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/retry/spring-retry/1.2.2.RELEASE/spring-retry-1.2.2.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-core/5.0.9.RELEASE/spring-core-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-jcl/5.0.9.RELEASE/spring-jcl-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-actuator/2.0.5.RELEASE/spring-boot-starter-actuator-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-actuator-autoconfigure/2.0.5.RELEASE/spring-boot-actuator-autoconfigure-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-actuator/2.0.5.RELEASE/spring-boot-actuator-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.6/jackson-datatype-jsr310-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/io/micrometer/micrometer-core/1.0.6/micrometer-core-1.0.6.jar, file:/C:/Users/weiyong/.m2/repository/org/hdrhistogram/HdrHistogram/2.1.10/HdrHistogram-2.1.10.jar, file:/C:/Users/weiyong/.m2/repository/org/latencyutils/LatencyUtils/2.0.3/LatencyUtils-2.0.3.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-bus-amqp/2.0.0.RELEASE/spring-cloud-starter-bus-amqp-2.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-stream-rabbit/2.0.1.RELEASE/spring-cloud-starter-stream-rabbit-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-stream-binder-rabbit/2.0.1.RELEASE/spring-cloud-stream-binder-rabbit-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-stream-binder-rabbit-core/2.0.1.RELEASE/spring-cloud-stream-binder-rabbit-core-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/rabbitmq/http-client/2.0.1.RELEASE/http-client-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-webflux/5.0.9.RELEASE/spring-webflux-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/projectreactor/ipc/reactor-netty/0.7.9.RELEASE/reactor-netty-0.7.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-codec-http/4.1.29.Final/netty-codec-http-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-handler-proxy/4.1.29.Final/netty-handler-proxy-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-codec-socks/4.1.29.Final/netty-codec-socks-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-transport-native-epoll/4.1.29.Final/netty-transport-native-epoll-4.1.29.Final-linux-x86_64.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-transport-native-unix-common/4.1.29.Final/netty-transport-native-unix-common-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-amqp/5.0.8.RELEASE/spring-integration-amqp-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-jmx/5.0.8.RELEASE/spring-integration-jmx-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-bus/2.0.0.RELEASE/spring-cloud-bus-2.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-stream/2.0.1.RELEASE/spring-cloud-stream-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-validation/2.0.5.RELEASE/spring-boot-starter-validation-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-tuple/1.0.0.RELEASE/spring-tuple-1.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/esotericsoftware/kryo-shaded/3.0.3/kryo-shaded-3.0.3.jar, file:/C:/Users/weiyong/.m2/repository/com/esotericsoftware/minlog/1.3.0/minlog-1.3.0.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-tuple/1.0.0.RELEASE/spring-integration-tuple-1.0.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/integration/spring-integration-core/5.0.8.RELEASE/spring-integration-core-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-data-redis/2.0.5.RELEASE/spring-boot-starter-data-redis-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-redis/2.0.10.RELEASE/spring-data-redis-2.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-keyvalue/2.0.10.RELEASE/spring-data-keyvalue-2.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-commons/2.0.10.RELEASE/spring-data-commons-2.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-oxm/5.0.9.RELEASE/spring-oxm-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/lettuce/lettuce-core/5.0.5.RELEASE/lettuce-core-5.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/projectreactor/reactor-core/3.1.9.RELEASE/reactor-core-3.1.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/reactivestreams/reactive-streams/1.0.2/reactive-streams-1.0.2.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-common/4.1.29.Final/netty-common-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-transport/4.1.29.Final/netty-transport-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-buffer/4.1.29.Final/netty-buffer-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-resolver/4.1.29.Final/netty-resolver-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-handler/4.1.29.Final/netty-handler-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty-codec/4.1.29.Final/netty-codec-4.1.29.Final.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-data-rest/2.0.5.RELEASE/spring-boot-starter-data-rest-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.0.5.RELEASE/spring-boot-starter-json-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.6/jackson-datatype-jdk8-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.6/jackson-module-parameter-names-2.9.6.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-rest-webmvc/3.0.10.RELEASE/spring-data-rest-webmvc-3.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/data/spring-data-rest-core/3.0.10.RELEASE/spring-data-rest-core-3.0.10.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/hateoas/spring-hateoas/0.25.0.RELEASE/spring-hateoas-0.25.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/plugin/spring-plugin-core/1.2.0.RELEASE/spring-plugin-core-1.2.0.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/atteo/evo-inflector/1.2.2/evo-inflector-1.2.2.jar, file:/C:/Users/weiyong/.m2/repository/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-jdbc/2.0.5.RELEASE/spring-boot-starter-jdbc-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/zaxxer/HikariCP/2.7.9/HikariCP-2.7.9.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-jdbc/5.0.9.RELEASE/spring-jdbc-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/xmlbeans/xmlbeans/2.6.0/xmlbeans-2.6.0.jar, file:/C:/Users/weiyong/.m2/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.0.5.RELEASE/spring-boot-starter-web-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.0.5.RELEASE/spring-boot-starter-tomcat-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/8.5.34/tomcat-embed-core-8.5.34.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/8.5.34/tomcat-embed-el-8.5.34.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/8.5.34/tomcat-embed-websocket-8.5.34.jar, file:/C:/Users/weiyong/.m2/repository/org/hibernate/validator/hibernate-validator/6.0.12.Final/hibernate-validator-6.0.12.Final.jar, file:/C:/Users/weiyong/.m2/repository/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar, file:/C:/Users/weiyong/.m2/repository/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar, file:/C:/Users/weiyong/.m2/repository/com/fasterxml/classmate/1.3.4/classmate-1.3.4.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-web/5.0.9.RELEASE/spring-web-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-webmvc/5.0.9.RELEASE/spring-webmvc-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-expression/5.0.9.RELEASE/spring-expression-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/spring/boot/mybatis-spring-boot-starter/1.3.2/mybatis-spring-boot-starter-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/1.3.2/mybatis-spring-boot-autoconfigure-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/mybatis/3.4.6/mybatis-3.4.6.jar, file:/C:/Users/weiyong/.m2/repository/org/mybatis/mybatis-spring/1.3.2/mybatis-spring-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/mysql/mysql-connector-java/5.1.47/mysql-connector-java-5.1.47.jar, file:/C:/Users/weiyong/.m2/repository/org/objenesis/objenesis/2.6/objenesis-2.6.jar, file:/C:/Users/weiyong/.m2/repository/org/projectlombok/lombok/1.16.10/lombok-1.16.10.jar, file:/C:/Users/weiyong/.m2/repository/com/alibaba/fastjson/1.2.29/fastjson-1.2.29.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-starter-openfeign/2.0.1.RELEASE/spring-cloud-starter-openfeign-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-openfeign-core/2.0.1.RELEASE/spring-cloud-openfeign-core-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-netflix-ribbon/2.0.1.RELEASE/spring-cloud-netflix-ribbon-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-netflix-archaius/2.0.1.RELEASE/spring-cloud-netflix-archaius-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/cloud/spring-cloud-commons/2.0.1.RELEASE/spring-cloud-commons-2.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/security/spring-security-crypto/5.0.8.RELEASE/spring-security-crypto-5.0.8.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-core/9.5.1/feign-core-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-slf4j/9.5.1/feign-slf4j-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-hystrix/9.5.1/feign-hystrix-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/com/netflix/archaius/archaius-core/0.7.6/archaius-core-0.7.6.jar, file:/C:/Users/weiyong/.m2/repository/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar, file:/C:/Users/weiyong/.m2/repository/commons-configuration/commons-configuration/1.8/commons-configuration-1.8.jar, file:/C:/Users/weiyong/.m2/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar, file:/C:/Users/weiyong/.m2/repository/com/netflix/hystrix/hystrix-core/1.5.12/hystrix-core-1.5.12.jar, file:/C:/Users/weiyong/.m2/repository/io/reactivex/rxjava/1.3.8/rxjava-1.3.8.jar, file:/C:/Users/weiyong/.m2/repository/io/github/openfeign/feign-java8/9.5.1/feign-java8-9.5.1.jar, file:/C:/Users/weiyong/.m2/repository/org/codehaus/groovy/groovy-all/2.4.12/groovy-all-2.4.12.jar, file:/C:/Users/weiyong/.m2/repository/javax/persistence/persistence-api/1.0/persistence-api-1.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/httpcomponents/httpclient/4.5.6/httpclient-4.5.6.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/httpcomponents/httpcore/4.4.10/httpcore-4.4.10.jar, file:/C:/Users/weiyong/.m2/repository/commons-codec/commons-codec/1.11/commons-codec-1.11.jar, file:/C:/Users/weiyong/.m2/repository/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar, file:/C:/Users/weiyong/.m2/repository/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar, file:/C:/Users/weiyong/.m2/repository/redis/clients/jedis/2.9.0/jedis-2.9.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-pool2/2.5.0/commons-pool2-2.5.0.jar, file:/C:/Users/weiyong/.m2/repository/commons-beanutils/commons-beanutils/1.9.3/commons-beanutils-1.9.3.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-lang3/3.8/commons-lang3-3.8.jar, file:/C:/Users/weiyong/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar, file:/C:/Users/weiyong/.m2/repository/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar, file:/C:/Users/weiyong/.m2/repository/nuxeo/nuxeo-common-xmap/1.0/nuxeo-common-xmap-1.0.jar, file:/C:/Users/weiyong/.m2/repository/com/alibaba/druid/1.0.15/druid-1.0.15.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/lib/jconsole.jar, file:/D:/Program%20Files/Java/jdk1.8.0_101/lib/tools.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/poi/poi/3.15/poi-3.15.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-collections4/4.1/commons-collections4-4.1.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/poi/poi-ooxml/3.15/poi-ooxml-3.15.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/poi/poi-ooxml-schemas/3.15/poi-ooxml-schemas-3.15.jar, file:/C:/Users/weiyong/.m2/repository/com/github/virtuald/curvesapi/1.04/curvesapi-1.04.jar, file:/C:/Users/weiyong/.m2/repository/com/google/protobuf/protobuf-java/2.5.0/protobuf-java-2.5.0.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/openservices/aliyun-log-logback-appender/0.1.13/aliyun-log-logback-appender-0.1.13.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/openservices/aliyun-log/0.6.10/aliyun-log-0.6.10.jar, file:/C:/Users/weiyong/.m2/repository/net/sf/json-lib/json-lib/2.4/json-lib-2.4-jdk15.jar, file:/C:/Users/weiyong/.m2/repository/net/sf/ezmorph/ezmorph/1.0.6/ezmorph-1.0.6.jar, file:/C:/Users/weiyong/.m2/repository/commons-validator/commons-validator/1.4.0/commons-validator-1.4.0.jar, file:/C:/Users/weiyong/.m2/repository/commons-digester/commons-digester/1.8/commons-digester-1.8.jar, file:/C:/Users/weiyong/.m2/repository/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/openservices/log-loghub-producer/0.1.14/log-loghub-producer-0.1.14.jar, file:/C:/Users/weiyong/.m2/repository/com/google/code/gson/gson/2.8.5/gson-2.8.5.jar, file:/C:/Users/weiyong/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar, file:/C:/Users/weiyong/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar, file:/C:/Users/weiyong/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar, file:/C:/Users/weiyong/.m2/repository/joda-time/joda-time/2.9.9/joda-time-2.9.9.jar, file:/C:/Users/weiyong/.m2/repository/commons-fileupload/commons-fileupload/1.3.2/commons-fileupload-1.3.2.jar, file:/C:/Users/weiyong/.m2/repository/commons-io/commons-io/2.2/commons-io-2.2.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-mail/2.0.5.RELEASE/spring-boot-starter-mail-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-context/5.0.9.RELEASE/spring-context-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/spring-context-support/5.0.9.RELEASE/spring-context-support-5.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar, file:/C:/Users/weiyong/.m2/repository/javax/activation/activation/1.1/activation-1.1.jar, file:/C:/Users/weiyong/.m2/repository/com/dangdang/elastic-job-lite-spring/2.1.5/elastic-job-lite-spring-2.1.5.jar, file:/C:/Users/weiyong/.m2/repository/com/dangdang/elastic-job-lite-core/2.1.5/elastic-job-lite-core-2.1.5.jar, file:/C:/Users/weiyong/.m2/repository/com/dangdang/elastic-job-common-core/2.1.5/elastic-job-common-core-2.1.5.jar, file:/C:/Users/weiyong/.m2/repository/org/quartz-scheduler/quartz/2.3.0/quartz-2.3.0.jar, file:/C:/Users/weiyong/.m2/repository/com/mchange/mchange-commons-java/0.2.11/mchange-commons-java-0.2.11.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/commons/commons-exec/1.3/commons-exec-1.3.jar, file:/C:/Users/weiyong/.m2/repository/com/aliyun/oss/aliyun-sdk-oss/2.8.3/aliyun-sdk-oss-2.8.3.jar, file:/C:/Users/weiyong/.m2/repository/org/jdom/jdom/1.1/jdom-1.1.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/curator/curator-framework/2.10.0/curator-framework-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/curator/curator-client/2.10.0/curator-client-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/zookeeper/zookeeper/3.4.6/zookeeper-3.4.6.jar, file:/C:/Users/weiyong/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar, file:/C:/Users/weiyong/.m2/repository/jline/jline/0.9.94/jline-0.9.94.jar, file:/C:/Users/weiyong/.m2/repository/io/netty/netty/3.7.0.Final/netty-3.7.0.Final.jar, file:/C:/Users/weiyong/.m2/repository/com/google/guava/guava/16.0.1/guava-16.0.1.jar, file:/C:/Users/weiyong/.m2/repository/org/apache/curator/curator-recipes/2.10.0/curator-recipes-2.10.0.jar, file:/C:/Users/weiyong/.m2/repository/org/springframework/boot/spring-boot-starter-thymeleaf/2.0.5.RELEASE/spring-boot-starter-thymeleaf-2.0.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/thymeleaf/thymeleaf-spring5/3.0.9.RELEASE/thymeleaf-spring5-3.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/thymeleaf/thymeleaf/3.0.9.RELEASE/thymeleaf-3.0.9.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/attoparser/attoparser/2.0.4.RELEASE/attoparser-2.0.4.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/unbescape/unbescape/1.1.5.RELEASE/unbescape-1.1.5.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/org/thymeleaf/extras/thymeleaf-extras-java8time/3.0.1.RELEASE/thymeleaf-extras-java8time-3.0.1.RELEASE.jar, file:/C:/Users/weiyong/.m2/repository/com/alibaba/zhima/alipay-sdk/java20150312151947/alipay-sdk-java20150312151947.jar, file:/D:/Program%20Files/JetBrains/IntelliJ%20IDEA%202017.3.1/lib/idea_rt.jar, file:/C:/Users/weiyong/.IntelliJIdea2017.3/system/captureAgent/debugger-agent.jar, file:/D:/Program%20Files/JetBrains/IntelliJ%20IDEA%202017.3.1/lib/asm-all.jar]
最后,再看一个 ServletWebServerApplicationContext 的refresh() , 其响应为:
// ServletWebServerApplicationContext.refresh() @Override public final void refresh() throws BeansException, IllegalStateException { try { super.refresh(); } catch (RuntimeException ex) { stopAndReleaseWebServer(); throw ex; } }
总体上来说,springboot 大量应用了事件监听机制,使各组件都基于事件运行,从而达到与框架解耦的作用;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?