001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot; 018 019import java.lang.reflect.Constructor; 020import java.security.AccessControlException; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.LinkedHashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Properties; 031import java.util.Set; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035 036import org.springframework.beans.BeanUtils; 037import org.springframework.beans.CachedIntrospectionResults; 038import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 039import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; 040import org.springframework.beans.factory.support.BeanDefinitionRegistry; 041import org.springframework.beans.factory.support.BeanNameGenerator; 042import org.springframework.beans.factory.support.DefaultListableBeanFactory; 043import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; 044import org.springframework.boot.Banner.Mode; 045import org.springframework.boot.context.properties.bind.Bindable; 046import org.springframework.boot.context.properties.bind.Binder; 047import org.springframework.boot.context.properties.source.ConfigurationPropertySources; 048import org.springframework.boot.convert.ApplicationConversionService; 049import org.springframework.boot.web.reactive.context.StandardReactiveWebEnvironment; 050import org.springframework.context.ApplicationContext; 051import org.springframework.context.ApplicationContextInitializer; 052import org.springframework.context.ApplicationListener; 053import org.springframework.context.ConfigurableApplicationContext; 054import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; 055import org.springframework.context.annotation.AnnotationConfigApplicationContext; 056import org.springframework.context.annotation.AnnotationConfigUtils; 057import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; 058import org.springframework.context.support.AbstractApplicationContext; 059import org.springframework.context.support.GenericApplicationContext; 060import org.springframework.core.GenericTypeResolver; 061import org.springframework.core.annotation.AnnotationAwareOrderComparator; 062import org.springframework.core.convert.ConversionService; 063import org.springframework.core.convert.support.ConfigurableConversionService; 064import org.springframework.core.env.CommandLinePropertySource; 065import org.springframework.core.env.CompositePropertySource; 066import org.springframework.core.env.ConfigurableEnvironment; 067import org.springframework.core.env.Environment; 068import org.springframework.core.env.MapPropertySource; 069import org.springframework.core.env.MutablePropertySources; 070import org.springframework.core.env.PropertySource; 071import org.springframework.core.env.SimpleCommandLinePropertySource; 072import org.springframework.core.env.StandardEnvironment; 073import org.springframework.core.io.DefaultResourceLoader; 074import org.springframework.core.io.ResourceLoader; 075import org.springframework.core.io.support.SpringFactoriesLoader; 076import org.springframework.util.Assert; 077import org.springframework.util.ClassUtils; 078import org.springframework.util.CollectionUtils; 079import org.springframework.util.ObjectUtils; 080import org.springframework.util.ReflectionUtils; 081import org.springframework.util.StopWatch; 082import org.springframework.util.StringUtils; 083import org.springframework.web.context.support.StandardServletEnvironment; 084 085/** 086 * Class that can be used to bootstrap and launch a Spring application from a Java main 087 * method. By default class will perform the following steps to bootstrap your 088 * application: 089 * 090 * <ul> 091 * <li>Create an appropriate {@link ApplicationContext} instance (depending on your 092 * classpath)</li> 093 * <li>Register a {@link CommandLinePropertySource} to expose command line arguments as 094 * Spring properties</li> 095 * <li>Refresh the application context, loading all singleton beans</li> 096 * <li>Trigger any {@link CommandLineRunner} beans</li> 097 * </ul> 098 * 099 * In most circumstances the static {@link #run(Class, String[])} method can be called 100 * directly from your {@literal main} method to bootstrap your application: 101 * 102 * <pre class="code"> 103 * @Configuration 104 * @EnableAutoConfiguration 105 * public class MyApplication { 106 * 107 * // ... Bean definitions 108 * 109 * public static void main(String[] args) throws Exception { 110 * SpringApplication.run(MyApplication.class, args); 111 * } 112 * } 113 * </pre> 114 * 115 * <p> 116 * For more advanced configuration a {@link SpringApplication} instance can be created and 117 * customized before being run: 118 * 119 * <pre class="code"> 120 * public static void main(String[] args) throws Exception { 121 * SpringApplication application = new SpringApplication(MyApplication.class); 122 * // ... customize application settings here 123 * application.run(args) 124 * } 125 * </pre> 126 * 127 * {@link SpringApplication}s can read beans from a variety of different sources. It is 128 * generally recommended that a single {@code @Configuration} class is used to bootstrap 129 * your application, however, you may also set {@link #getSources() sources} from: 130 * <ul> 131 * <li>The fully qualified class name to be loaded by 132 * {@link AnnotatedBeanDefinitionReader}</li> 133 * <li>The location of an XML resource to be loaded by {@link XmlBeanDefinitionReader}, or 134 * a groovy script to be loaded by {@link GroovyBeanDefinitionReader}</li> 135 * <li>The name of a package to be scanned by {@link ClassPathBeanDefinitionScanner}</li> 136 * </ul> 137 * 138 * Configuration properties are also bound to the {@link SpringApplication}. This makes it 139 * possible to set {@link SpringApplication} properties dynamically, like additional 140 * sources ("spring.main.sources" - a CSV list) the flag to indicate a web environment 141 * ("spring.main.web-application-type=none") or the flag to switch off the banner 142 * ("spring.main.banner-mode=off"). 143 * 144 * @author Phillip Webb 145 * @author Dave Syer 146 * @author Andy Wilkinson 147 * @author Christian Dupuis 148 * @author Stephane Nicoll 149 * @author Jeremy Rickard 150 * @author Craig Burke 151 * @author Michael Simons 152 * @author Madhura Bhave 153 * @author Brian Clozel 154 * @author Ethan Rubinson 155 * @see #run(Class, String[]) 156 * @see #run(Class[], String[]) 157 * @see #SpringApplication(Class...) 158 */ 159public class SpringApplication { 160 161 /** 162 * The class name of application context that will be used by default for non-web 163 * environments. 164 */ 165 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." 166 + "annotation.AnnotationConfigApplicationContext"; 167 168 /** 169 * The class name of application context that will be used by default for web 170 * environments. 171 */ 172 public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." 173 + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; 174 175 /** 176 * The class name of application context that will be used by default for reactive web 177 * environments. 178 */ 179 public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." 180 + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; 181 182 /** 183 * Default banner location. 184 */ 185 public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION; 186 187 /** 188 * Banner location property key. 189 */ 190 public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY; 191 192 private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; 193 194 private static final Log logger = LogFactory.getLog(SpringApplication.class); 195 196 private Set<Class<?>> primarySources; 197 198 private Set<String> sources = new LinkedHashSet<>(); 199 200 private Class<?> mainApplicationClass; 201 202 private Banner.Mode bannerMode = Banner.Mode.CONSOLE; 203 204 private boolean logStartupInfo = true; 205 206 private boolean addCommandLineProperties = true; 207 208 private boolean addConversionService = true; 209 210 private Banner banner; 211 212 private ResourceLoader resourceLoader; 213 214 private BeanNameGenerator beanNameGenerator; 215 216 private ConfigurableEnvironment environment; 217 218 private Class<? extends ConfigurableApplicationContext> applicationContextClass; 219 220 private WebApplicationType webApplicationType; 221 222 private boolean headless = true; 223 224 private boolean registerShutdownHook = true; 225 226 private List<ApplicationContextInitializer<?>> initializers; 227 228 private List<ApplicationListener<?>> listeners; 229 230 private Map<String, Object> defaultProperties; 231 232 private Set<String> additionalProfiles = new HashSet<>(); 233 234 private boolean allowBeanDefinitionOverriding; 235 236 private boolean isCustomEnvironment = false; 237 238 /** 239 * Create a new {@link SpringApplication} instance. The application context will load 240 * beans from the specified primary sources (see {@link SpringApplication class-level} 241 * documentation for details. The instance can be customized before calling 242 * {@link #run(String...)}. 243 * @param primarySources the primary bean sources 244 * @see #run(Class, String[]) 245 * @see #SpringApplication(ResourceLoader, Class...) 246 * @see #setSources(Set) 247 */ 248 public SpringApplication(Class<?>... primarySources) { 249 this(null, primarySources); 250 } 251 252 /** 253 * Create a new {@link SpringApplication} instance. The application context will load 254 * beans from the specified primary sources (see {@link SpringApplication class-level} 255 * documentation for details. The instance can be customized before calling 256 * {@link #run(String...)}. 257 * @param resourceLoader the resource loader to use 258 * @param primarySources the primary bean sources 259 * @see #run(Class, String[]) 260 * @see #setSources(Set) 261 */ 262 @SuppressWarnings({ "unchecked", "rawtypes" }) 263 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 264 this.resourceLoader = resourceLoader; 265 Assert.notNull(primarySources, "PrimarySources must not be null"); 266 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 267 this.webApplicationType = WebApplicationType.deduceFromClasspath(); 268 setInitializers((Collection) getSpringFactoriesInstances( 269 ApplicationContextInitializer.class)); 270 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 271 this.mainApplicationClass = deduceMainApplicationClass(); 272 } 273 274 private Class<?> deduceMainApplicationClass() { 275 try { 276 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); 277 for (StackTraceElement stackTraceElement : stackTrace) { 278 if ("main".equals(stackTraceElement.getMethodName())) { 279 return Class.forName(stackTraceElement.getClassName()); 280 } 281 } 282 } 283 catch (ClassNotFoundException ex) { 284 // Swallow and continue 285 } 286 return null; 287 } 288 289 /** 290 * Run the Spring application, creating and refreshing a new 291 * {@link ApplicationContext}. 292 * @param args the application arguments (usually passed from a Java main method) 293 * @return a running {@link ApplicationContext} 294 */ 295 public ConfigurableApplicationContext run(String... args) { 296 StopWatch stopWatch = new StopWatch(); 297 stopWatch.start(); 298 ConfigurableApplicationContext context = null; 299 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 300 configureHeadlessProperty(); 301 SpringApplicationRunListeners listeners = getRunListeners(args); 302 listeners.starting(); 303 try { 304 ApplicationArguments applicationArguments = new DefaultApplicationArguments( 305 args); 306 ConfigurableEnvironment environment = prepareEnvironment(listeners, 307 applicationArguments); 308 configureIgnoreBeanInfo(environment); 309 Banner printedBanner = printBanner(environment); 310 context = createApplicationContext(); 311 exceptionReporters = getSpringFactoriesInstances( 312 SpringBootExceptionReporter.class, 313 new Class[] { ConfigurableApplicationContext.class }, context); 314 prepareContext(context, environment, listeners, applicationArguments, 315 printedBanner); 316 refreshContext(context); 317 afterRefresh(context, applicationArguments); 318 stopWatch.stop(); 319 if (this.logStartupInfo) { 320 new StartupInfoLogger(this.mainApplicationClass) 321 .logStarted(getApplicationLog(), stopWatch); 322 } 323 listeners.started(context); 324 callRunners(context, applicationArguments); 325 } 326 catch (Throwable ex) { 327 handleRunFailure(context, ex, exceptionReporters, listeners); 328 throw new IllegalStateException(ex); 329 } 330 331 try { 332 listeners.running(context); 333 } 334 catch (Throwable ex) { 335 handleRunFailure(context, ex, exceptionReporters, null); 336 throw new IllegalStateException(ex); 337 } 338 return context; 339 } 340 341 private ConfigurableEnvironment prepareEnvironment( 342 SpringApplicationRunListeners listeners, 343 ApplicationArguments applicationArguments) { 344 // Create and configure the environment 345 ConfigurableEnvironment environment = getOrCreateEnvironment(); 346 configureEnvironment(environment, applicationArguments.getSourceArgs()); 347 listeners.environmentPrepared(environment); 348 bindToSpringApplication(environment); 349 if (!this.isCustomEnvironment) { 350 environment = new EnvironmentConverter(getClassLoader()) 351 .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); 352 } 353 ConfigurationPropertySources.attach(environment); 354 return environment; 355 } 356 357 private Class<? extends StandardEnvironment> deduceEnvironmentClass() { 358 switch (this.webApplicationType) { 359 case SERVLET: 360 return StandardServletEnvironment.class; 361 case REACTIVE: 362 return StandardReactiveWebEnvironment.class; 363 default: 364 return StandardEnvironment.class; 365 } 366 } 367 368 private void prepareContext(ConfigurableApplicationContext context, 369 ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, 370 ApplicationArguments applicationArguments, Banner printedBanner) { 371 context.setEnvironment(environment); 372 postProcessApplicationContext(context); 373 applyInitializers(context); 374 listeners.contextPrepared(context); 375 if (this.logStartupInfo) { 376 logStartupInfo(context.getParent() == null); 377 logStartupProfileInfo(context); 378 } 379 // Add boot specific singleton beans 380 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 381 beanFactory.registerSingleton("springApplicationArguments", applicationArguments); 382 if (printedBanner != null) { 383 beanFactory.registerSingleton("springBootBanner", printedBanner); 384 } 385 if (beanFactory instanceof DefaultListableBeanFactory) { 386 ((DefaultListableBeanFactory) beanFactory) 387 .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); 388 } 389 // Load the sources 390 Set<Object> sources = getAllSources(); 391 Assert.notEmpty(sources, "Sources must not be empty"); 392 load(context, sources.toArray(new Object[0])); 393 listeners.contextLoaded(context); 394 } 395 396 private void refreshContext(ConfigurableApplicationContext context) { 397 refresh(context); 398 if (this.registerShutdownHook) { 399 try { 400 context.registerShutdownHook(); 401 } 402 catch (AccessControlException ex) { 403 // Not allowed in some environments. 404 } 405 } 406 } 407 408 private void configureHeadlessProperty() { 409 System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty( 410 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); 411 } 412 413 private SpringApplicationRunListeners getRunListeners(String[] args) { 414 Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; 415 return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( 416 SpringApplicationRunListener.class, types, this, args)); 417 } 418 419 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { 420 return getSpringFactoriesInstances(type, new Class<?>[] {}); 421 } 422 423 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, 424 Class<?>[] parameterTypes, Object... args) { 425 ClassLoader classLoader = getClassLoader(); 426 // Use names and ensure unique to protect against duplicates 427 Set<String> names = new LinkedHashSet<>( 428 SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 429 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, 430 classLoader, args, names); 431 AnnotationAwareOrderComparator.sort(instances); 432 return instances; 433 } 434 435 @SuppressWarnings("unchecked") 436 private <T> List<T> createSpringFactoriesInstances(Class<T> type, 437 Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, 438 Set<String> names) { 439 List<T> instances = new ArrayList<>(names.size()); 440 for (String name : names) { 441 try { 442 Class<?> instanceClass = ClassUtils.forName(name, classLoader); 443 Assert.isAssignable(type, instanceClass); 444 Constructor<?> constructor = instanceClass 445 .getDeclaredConstructor(parameterTypes); 446 T instance = (T) BeanUtils.instantiateClass(constructor, args); 447 instances.add(instance); 448 } 449 catch (Throwable ex) { 450 throw new IllegalArgumentException( 451 "Cannot instantiate " + type + " : " + name, ex); 452 } 453 } 454 return instances; 455 } 456 457 private ConfigurableEnvironment getOrCreateEnvironment() { 458 if (this.environment != null) { 459 return this.environment; 460 } 461 switch (this.webApplicationType) { 462 case SERVLET: 463 return new StandardServletEnvironment(); 464 case REACTIVE: 465 return new StandardReactiveWebEnvironment(); 466 default: 467 return new StandardEnvironment(); 468 } 469 } 470 471 /** 472 * Template method delegating to 473 * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and 474 * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order. 475 * Override this method for complete control over Environment customization, or one of 476 * the above for fine-grained control over property sources or profiles, respectively. 477 * @param environment this application's environment 478 * @param args arguments passed to the {@code run} method 479 * @see #configureProfiles(ConfigurableEnvironment, String[]) 480 * @see #configurePropertySources(ConfigurableEnvironment, String[]) 481 */ 482 protected void configureEnvironment(ConfigurableEnvironment environment, 483 String[] args) { 484 if (this.addConversionService) { 485 ConversionService conversionService = ApplicationConversionService 486 .getSharedInstance(); 487 environment.setConversionService( 488 (ConfigurableConversionService) conversionService); 489 } 490 configurePropertySources(environment, args); 491 configureProfiles(environment, args); 492 } 493 494 /** 495 * Add, remove or re-order any {@link PropertySource}s in this application's 496 * environment. 497 * @param environment this application's environment 498 * @param args arguments passed to the {@code run} method 499 * @see #configureEnvironment(ConfigurableEnvironment, String[]) 500 */ 501 protected void configurePropertySources(ConfigurableEnvironment environment, 502 String[] args) { 503 MutablePropertySources sources = environment.getPropertySources(); 504 if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { 505 sources.addLast( 506 new MapPropertySource("defaultProperties", this.defaultProperties)); 507 } 508 if (this.addCommandLineProperties && args.length > 0) { 509 String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; 510 if (sources.contains(name)) { 511 PropertySource<?> source = sources.get(name); 512 CompositePropertySource composite = new CompositePropertySource(name); 513 composite.addPropertySource(new SimpleCommandLinePropertySource( 514 "springApplicationCommandLineArgs", args)); 515 composite.addPropertySource(source); 516 sources.replace(name, composite); 517 } 518 else { 519 sources.addFirst(new SimpleCommandLinePropertySource(args)); 520 } 521 } 522 } 523 524 /** 525 * Configure which profiles are active (or active by default) for this application 526 * environment. Additional profiles may be activated during configuration file 527 * processing via the {@code spring.profiles.active} property. 528 * @param environment this application's environment 529 * @param args arguments passed to the {@code run} method 530 * @see #configureEnvironment(ConfigurableEnvironment, String[]) 531 * @see org.springframework.boot.context.config.ConfigFileApplicationListener 532 */ 533 protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { 534 environment.getActiveProfiles(); // ensure they are initialized 535 // But these ones should go first (last wins in a property key clash) 536 Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles); 537 profiles.addAll(Arrays.asList(environment.getActiveProfiles())); 538 environment.setActiveProfiles(StringUtils.toStringArray(profiles)); 539 } 540 541 private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { 542 if (System.getProperty( 543 CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { 544 Boolean ignore = environment.getProperty("spring.beaninfo.ignore", 545 Boolean.class, Boolean.TRUE); 546 System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, 547 ignore.toString()); 548 } 549 } 550 551 /** 552 * Bind the environment to the {@link SpringApplication}. 553 * @param environment the environment to bind 554 */ 555 protected void bindToSpringApplication(ConfigurableEnvironment environment) { 556 try { 557 Binder.get(environment).bind("spring.main", Bindable.ofInstance(this)); 558 } 559 catch (Exception ex) { 560 throw new IllegalStateException("Cannot bind to SpringApplication", ex); 561 } 562 } 563 564 private Banner printBanner(ConfigurableEnvironment environment) { 565 if (this.bannerMode == Banner.Mode.OFF) { 566 return null; 567 } 568 ResourceLoader resourceLoader = (this.resourceLoader != null) 569 ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); 570 SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter( 571 resourceLoader, this.banner); 572 if (this.bannerMode == Mode.LOG) { 573 return bannerPrinter.print(environment, this.mainApplicationClass, logger); 574 } 575 return bannerPrinter.print(environment, this.mainApplicationClass, System.out); 576 } 577 578 /** 579 * Strategy method used to create the {@link ApplicationContext}. By default this 580 * method will respect any explicitly set application context or application context 581 * class before falling back to a suitable default. 582 * @return the application context (not yet refreshed) 583 * @see #setApplicationContextClass(Class) 584 */ 585 protected ConfigurableApplicationContext createApplicationContext() { 586 Class<?> contextClass = this.applicationContextClass; 587 if (contextClass == null) { 588 try { 589 switch (this.webApplicationType) { 590 case SERVLET: 591 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); 592 break; 593 case REACTIVE: 594 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); 595 break; 596 default: 597 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); 598 } 599 } 600 catch (ClassNotFoundException ex) { 601 throw new IllegalStateException( 602 "Unable create a default ApplicationContext, " 603 + "please specify an ApplicationContextClass", 604 ex); 605 } 606 } 607 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); 608 } 609 610 /** 611 * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can 612 * apply additional processing as required. 613 * @param context the application context 614 */ 615 protected void postProcessApplicationContext(ConfigurableApplicationContext context) { 616 if (this.beanNameGenerator != null) { 617 context.getBeanFactory().registerSingleton( 618 AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, 619 this.beanNameGenerator); 620 } 621 if (this.resourceLoader != null) { 622 if (context instanceof GenericApplicationContext) { 623 ((GenericApplicationContext) context) 624 .setResourceLoader(this.resourceLoader); 625 } 626 if (context instanceof DefaultResourceLoader) { 627 ((DefaultResourceLoader) context) 628 .setClassLoader(this.resourceLoader.getClassLoader()); 629 } 630 } 631 if (this.addConversionService) { 632 context.getBeanFactory().setConversionService( 633 ApplicationConversionService.getSharedInstance()); 634 } 635 } 636 637 /** 638 * Apply any {@link ApplicationContextInitializer}s to the context before it is 639 * refreshed. 640 * @param context the configured ApplicationContext (not refreshed yet) 641 * @see ConfigurableApplicationContext#refresh() 642 */ 643 @SuppressWarnings({ "rawtypes", "unchecked" }) 644 protected void applyInitializers(ConfigurableApplicationContext context) { 645 for (ApplicationContextInitializer initializer : getInitializers()) { 646 Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( 647 initializer.getClass(), ApplicationContextInitializer.class); 648 Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); 649 initializer.initialize(context); 650 } 651 } 652 653 /** 654 * Called to log startup information, subclasses may override to add additional 655 * logging. 656 * @param isRoot true if this application is the root of a context hierarchy 657 */ 658 protected void logStartupInfo(boolean isRoot) { 659 if (isRoot) { 660 new StartupInfoLogger(this.mainApplicationClass) 661 .logStarting(getApplicationLog()); 662 } 663 } 664 665 /** 666 * Called to log active profile information. 667 * @param context the application context 668 */ 669 protected void logStartupProfileInfo(ConfigurableApplicationContext context) { 670 Log log = getApplicationLog(); 671 if (log.isInfoEnabled()) { 672 String[] activeProfiles = context.getEnvironment().getActiveProfiles(); 673 if (ObjectUtils.isEmpty(activeProfiles)) { 674 String[] defaultProfiles = context.getEnvironment().getDefaultProfiles(); 675 log.info("No active profile set, falling back to default profiles: " 676 + StringUtils.arrayToCommaDelimitedString(defaultProfiles)); 677 } 678 else { 679 log.info("The following profiles are active: " 680 + StringUtils.arrayToCommaDelimitedString(activeProfiles)); 681 } 682 } 683 } 684 685 /** 686 * Returns the {@link Log} for the application. By default will be deduced. 687 * @return the application log 688 */ 689 protected Log getApplicationLog() { 690 if (this.mainApplicationClass == null) { 691 return logger; 692 } 693 return LogFactory.getLog(this.mainApplicationClass); 694 } 695 696 /** 697 * Load beans into the application context. 698 * @param context the context to load beans into 699 * @param sources the sources to load 700 */ 701 protected void load(ApplicationContext context, Object[] sources) { 702 if (logger.isDebugEnabled()) { 703 logger.debug( 704 "Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); 705 } 706 BeanDefinitionLoader loader = createBeanDefinitionLoader( 707 getBeanDefinitionRegistry(context), sources); 708 if (this.beanNameGenerator != null) { 709 loader.setBeanNameGenerator(this.beanNameGenerator); 710 } 711 if (this.resourceLoader != null) { 712 loader.setResourceLoader(this.resourceLoader); 713 } 714 if (this.environment != null) { 715 loader.setEnvironment(this.environment); 716 } 717 loader.load(); 718 } 719 720 /** 721 * The ResourceLoader that will be used in the ApplicationContext. 722 * @return the resourceLoader the resource loader that will be used in the 723 * ApplicationContext (or null if the default) 724 */ 725 public ResourceLoader getResourceLoader() { 726 return this.resourceLoader; 727 } 728 729 /** 730 * Either the ClassLoader that will be used in the ApplicationContext (if 731 * {@link #setResourceLoader(ResourceLoader) resourceLoader} is set, or the context 732 * class loader (if not null), or the loader of the Spring {@link ClassUtils} class. 733 * @return a ClassLoader (never null) 734 */ 735 public ClassLoader getClassLoader() { 736 if (this.resourceLoader != null) { 737 return this.resourceLoader.getClassLoader(); 738 } 739 return ClassUtils.getDefaultClassLoader(); 740 } 741 742 /** 743 * Get the bean definition registry. 744 * @param context the application context 745 * @return the BeanDefinitionRegistry if it can be determined 746 */ 747 private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) { 748 if (context instanceof BeanDefinitionRegistry) { 749 return (BeanDefinitionRegistry) context; 750 } 751 if (context instanceof AbstractApplicationContext) { 752 return (BeanDefinitionRegistry) ((AbstractApplicationContext) context) 753 .getBeanFactory(); 754 } 755 throw new IllegalStateException("Could not locate BeanDefinitionRegistry"); 756 } 757 758 /** 759 * Factory method used to create the {@link BeanDefinitionLoader}. 760 * @param registry the bean definition registry 761 * @param sources the sources to load 762 * @return the {@link BeanDefinitionLoader} that will be used to load beans 763 */ 764 protected BeanDefinitionLoader createBeanDefinitionLoader( 765 BeanDefinitionRegistry registry, Object[] sources) { 766 return new BeanDefinitionLoader(registry, sources); 767 } 768 769 /** 770 * Refresh the underlying {@link ApplicationContext}. 771 * @param applicationContext the application context to refresh 772 */ 773 protected void refresh(ApplicationContext applicationContext) { 774 Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); 775 ((AbstractApplicationContext) applicationContext).refresh(); 776 } 777 778 /** 779 * Called after the context has been refreshed. 780 * @param context the application context 781 * @param args the application arguments 782 */ 783 protected void afterRefresh(ConfigurableApplicationContext context, 784 ApplicationArguments args) { 785 } 786 787 private void callRunners(ApplicationContext context, ApplicationArguments args) { 788 List<Object> runners = new ArrayList<>(); 789 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); 790 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); 791 AnnotationAwareOrderComparator.sort(runners); 792 for (Object runner : new LinkedHashSet<>(runners)) { 793 if (runner instanceof ApplicationRunner) { 794 callRunner((ApplicationRunner) runner, args); 795 } 796 if (runner instanceof CommandLineRunner) { 797 callRunner((CommandLineRunner) runner, args); 798 } 799 } 800 } 801 802 private void callRunner(ApplicationRunner runner, ApplicationArguments args) { 803 try { 804 (runner).run(args); 805 } 806 catch (Exception ex) { 807 throw new IllegalStateException("Failed to execute ApplicationRunner", ex); 808 } 809 } 810 811 private void callRunner(CommandLineRunner runner, ApplicationArguments args) { 812 try { 813 (runner).run(args.getSourceArgs()); 814 } 815 catch (Exception ex) { 816 throw new IllegalStateException("Failed to execute CommandLineRunner", ex); 817 } 818 } 819 820 private void handleRunFailure(ConfigurableApplicationContext context, 821 Throwable exception, 822 Collection<SpringBootExceptionReporter> exceptionReporters, 823 SpringApplicationRunListeners listeners) { 824 try { 825 try { 826 handleExitCode(context, exception); 827 if (listeners != null) { 828 listeners.failed(context, exception); 829 } 830 } 831 finally { 832 reportFailure(exceptionReporters, exception); 833 if (context != null) { 834 context.close(); 835 } 836 } 837 } 838 catch (Exception ex) { 839 logger.warn("Unable to close ApplicationContext", ex); 840 } 841 ReflectionUtils.rethrowRuntimeException(exception); 842 } 843 844 private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, 845 Throwable failure) { 846 try { 847 for (SpringBootExceptionReporter reporter : exceptionReporters) { 848 if (reporter.reportException(failure)) { 849 registerLoggedException(failure); 850 return; 851 } 852 } 853 } 854 catch (Throwable ex) { 855 // Continue with normal handling of the original failure 856 } 857 if (logger.isErrorEnabled()) { 858 logger.error("Application run failed", failure); 859 registerLoggedException(failure); 860 } 861 } 862 863 /** 864 * Register that the given exception has been logged. By default, if the running in 865 * the main thread, this method will suppress additional printing of the stacktrace. 866 * @param exception the exception that was logged 867 */ 868 protected void registerLoggedException(Throwable exception) { 869 SpringBootExceptionHandler handler = getSpringBootExceptionHandler(); 870 if (handler != null) { 871 handler.registerLoggedException(exception); 872 } 873 } 874 875 private void handleExitCode(ConfigurableApplicationContext context, 876 Throwable exception) { 877 int exitCode = getExitCodeFromException(context, exception); 878 if (exitCode != 0) { 879 if (context != null) { 880 context.publishEvent(new ExitCodeEvent(context, exitCode)); 881 } 882 SpringBootExceptionHandler handler = getSpringBootExceptionHandler(); 883 if (handler != null) { 884 handler.registerExitCode(exitCode); 885 } 886 } 887 } 888 889 private int getExitCodeFromException(ConfigurableApplicationContext context, 890 Throwable exception) { 891 int exitCode = getExitCodeFromMappedException(context, exception); 892 if (exitCode == 0) { 893 exitCode = getExitCodeFromExitCodeGeneratorException(exception); 894 } 895 return exitCode; 896 } 897 898 private int getExitCodeFromMappedException(ConfigurableApplicationContext context, 899 Throwable exception) { 900 if (context == null || !context.isActive()) { 901 return 0; 902 } 903 ExitCodeGenerators generators = new ExitCodeGenerators(); 904 Collection<ExitCodeExceptionMapper> beans = context 905 .getBeansOfType(ExitCodeExceptionMapper.class).values(); 906 generators.addAll(exception, beans); 907 return generators.getExitCode(); 908 } 909 910 private int getExitCodeFromExitCodeGeneratorException(Throwable exception) { 911 if (exception == null) { 912 return 0; 913 } 914 if (exception instanceof ExitCodeGenerator) { 915 return ((ExitCodeGenerator) exception).getExitCode(); 916 } 917 return getExitCodeFromExitCodeGeneratorException(exception.getCause()); 918 } 919 920 SpringBootExceptionHandler getSpringBootExceptionHandler() { 921 if (isMainThread(Thread.currentThread())) { 922 return SpringBootExceptionHandler.forCurrentThread(); 923 } 924 return null; 925 } 926 927 private boolean isMainThread(Thread currentThread) { 928 return ("main".equals(currentThread.getName()) 929 || "restartedMain".equals(currentThread.getName())) 930 && "main".equals(currentThread.getThreadGroup().getName()); 931 } 932 933 /** 934 * Returns the main application class that has been deduced or explicitly configured. 935 * @return the main application class or {@code null} 936 */ 937 public Class<?> getMainApplicationClass() { 938 return this.mainApplicationClass; 939 } 940 941 /** 942 * Set a specific main application class that will be used as a log source and to 943 * obtain version information. By default the main application class will be deduced. 944 * Can be set to {@code null} if there is no explicit application class. 945 * @param mainApplicationClass the mainApplicationClass to set or {@code null} 946 */ 947 public void setMainApplicationClass(Class<?> mainApplicationClass) { 948 this.mainApplicationClass = mainApplicationClass; 949 } 950 951 /** 952 * Returns the type of web application that is being run. 953 * @return the type of web application 954 * @since 2.0.0 955 */ 956 public WebApplicationType getWebApplicationType() { 957 return this.webApplicationType; 958 } 959 960 /** 961 * Sets the type of web application to be run. If not explicitly set the type of web 962 * application will be deduced based on the classpath. 963 * @param webApplicationType the web application type 964 * @since 2.0.0 965 */ 966 public void setWebApplicationType(WebApplicationType webApplicationType) { 967 Assert.notNull(webApplicationType, "WebApplicationType must not be null"); 968 this.webApplicationType = webApplicationType; 969 } 970 971 /** 972 * Sets if bean definition overriding, by registering a definition with the same name 973 * as an existing definition, should be allowed. Defaults to {@code false}. 974 * @param allowBeanDefinitionOverriding if overriding is allowed 975 * @since 2.1 976 * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding(boolean) 977 */ 978 public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { 979 this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding; 980 } 981 982 /** 983 * Sets if the application is headless and should not instantiate AWT. Defaults to 984 * {@code true} to prevent java icons appearing. 985 * @param headless if the application is headless 986 */ 987 public void setHeadless(boolean headless) { 988 this.headless = headless; 989 } 990 991 /** 992 * Sets if the created {@link ApplicationContext} should have a shutdown hook 993 * registered. Defaults to {@code true} to ensure that JVM shutdowns are handled 994 * gracefully. 995 * @param registerShutdownHook if the shutdown hook should be registered 996 */ 997 public void setRegisterShutdownHook(boolean registerShutdownHook) { 998 this.registerShutdownHook = registerShutdownHook; 999 } 1000 1001 /** 1002 * Sets the {@link Banner} instance which will be used to print the banner when no 1003 * static banner file is provided. 1004 * @param banner the Banner instance to use 1005 */ 1006 public void setBanner(Banner banner) { 1007 this.banner = banner; 1008 } 1009 1010 /** 1011 * Sets the mode used to display the banner when the application runs. Defaults to 1012 * {@code Banner.Mode.CONSOLE}. 1013 * @param bannerMode the mode used to display the banner 1014 */ 1015 public void setBannerMode(Banner.Mode bannerMode) { 1016 this.bannerMode = bannerMode; 1017 } 1018 1019 /** 1020 * Sets if the application information should be logged when the application starts. 1021 * Defaults to {@code true}. 1022 * @param logStartupInfo if startup info should be logged. 1023 */ 1024 public void setLogStartupInfo(boolean logStartupInfo) { 1025 this.logStartupInfo = logStartupInfo; 1026 } 1027 1028 /** 1029 * Sets if a {@link CommandLinePropertySource} should be added to the application 1030 * context in order to expose arguments. Defaults to {@code true}. 1031 * @param addCommandLineProperties if command line arguments should be exposed 1032 */ 1033 public void setAddCommandLineProperties(boolean addCommandLineProperties) { 1034 this.addCommandLineProperties = addCommandLineProperties; 1035 } 1036 1037 /** 1038 * Sets if the {@link ApplicationConversionService} should be added to the application 1039 * context's {@link Environment}. 1040 * @param addConversionService if the application conversion service should be added 1041 * @since 2.1.0 1042 */ 1043 public void setAddConversionService(boolean addConversionService) { 1044 this.addConversionService = addConversionService; 1045 } 1046 1047 /** 1048 * Set default environment properties which will be used in addition to those in the 1049 * existing {@link Environment}. 1050 * @param defaultProperties the additional properties to set 1051 */ 1052 public void setDefaultProperties(Map<String, Object> defaultProperties) { 1053 this.defaultProperties = defaultProperties; 1054 } 1055 1056 /** 1057 * Convenient alternative to {@link #setDefaultProperties(Map)}. 1058 * @param defaultProperties some {@link Properties} 1059 */ 1060 public void setDefaultProperties(Properties defaultProperties) { 1061 this.defaultProperties = new HashMap<>(); 1062 for (Object key : Collections.list(defaultProperties.propertyNames())) { 1063 this.defaultProperties.put((String) key, defaultProperties.get(key)); 1064 } 1065 } 1066 1067 /** 1068 * Set additional profile values to use (on top of those set in system or command line 1069 * properties). 1070 * @param profiles the additional profiles to set 1071 */ 1072 public void setAdditionalProfiles(String... profiles) { 1073 this.additionalProfiles = new LinkedHashSet<>(Arrays.asList(profiles)); 1074 } 1075 1076 /** 1077 * Sets the bean name generator that should be used when generating bean names. 1078 * @param beanNameGenerator the bean name generator 1079 */ 1080 public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { 1081 this.beanNameGenerator = beanNameGenerator; 1082 } 1083 1084 /** 1085 * Sets the underlying environment that should be used with the created application 1086 * context. 1087 * @param environment the environment 1088 */ 1089 public void setEnvironment(ConfigurableEnvironment environment) { 1090 this.isCustomEnvironment = true; 1091 this.environment = environment; 1092 } 1093 1094 /** 1095 * Add additional items to the primary sources that will be added to an 1096 * ApplicationContext when {@link #run(String...)} is called. 1097 * <p> 1098 * The sources here are added to those that were set in the constructor. Most users 1099 * should consider using {@link #getSources()}/{@link #setSources(Set)} rather than 1100 * calling this method. 1101 * @param additionalPrimarySources the additional primary sources to add 1102 * @see #SpringApplication(Class...) 1103 * @see #getSources() 1104 * @see #setSources(Set) 1105 * @see #getAllSources() 1106 */ 1107 public void addPrimarySources(Collection<Class<?>> additionalPrimarySources) { 1108 this.primarySources.addAll(additionalPrimarySources); 1109 } 1110 1111 /** 1112 * Returns a mutable set of the sources that will be added to an ApplicationContext 1113 * when {@link #run(String...)} is called. 1114 * <p> 1115 * Sources set here will be used in addition to any primary sources set in the 1116 * constructor. 1117 * @return the application sources. 1118 * @see #SpringApplication(Class...) 1119 * @see #getAllSources() 1120 */ 1121 public Set<String> getSources() { 1122 return this.sources; 1123 } 1124 1125 /** 1126 * Set additional sources that will be used to create an ApplicationContext. A source 1127 * can be: a class name, package name, or an XML resource location. 1128 * <p> 1129 * Sources set here will be used in addition to any primary sources set in the 1130 * constructor. 1131 * @param sources the application sources to set 1132 * @see #SpringApplication(Class...) 1133 * @see #getAllSources() 1134 */ 1135 public void setSources(Set<String> sources) { 1136 Assert.notNull(sources, "Sources must not be null"); 1137 this.sources = new LinkedHashSet<>(sources); 1138 } 1139 1140 /** 1141 * Return an immutable set of all the sources that will be added to an 1142 * ApplicationContext when {@link #run(String...)} is called. This method combines any 1143 * primary sources specified in the constructor with any additional ones that have 1144 * been {@link #setSources(Set) explicitly set}. 1145 * @return an immutable set of all sources 1146 */ 1147 public Set<Object> getAllSources() { 1148 Set<Object> allSources = new LinkedHashSet<>(); 1149 if (!CollectionUtils.isEmpty(this.primarySources)) { 1150 allSources.addAll(this.primarySources); 1151 } 1152 if (!CollectionUtils.isEmpty(this.sources)) { 1153 allSources.addAll(this.sources); 1154 } 1155 return Collections.unmodifiableSet(allSources); 1156 } 1157 1158 /** 1159 * Sets the {@link ResourceLoader} that should be used when loading resources. 1160 * @param resourceLoader the resource loader 1161 */ 1162 public void setResourceLoader(ResourceLoader resourceLoader) { 1163 Assert.notNull(resourceLoader, "ResourceLoader must not be null"); 1164 this.resourceLoader = resourceLoader; 1165 } 1166 1167 /** 1168 * Sets the type of Spring {@link ApplicationContext} that will be created. If not 1169 * specified defaults to {@link #DEFAULT_SERVLET_WEB_CONTEXT_CLASS} for web based 1170 * applications or {@link AnnotationConfigApplicationContext} for non web based 1171 * applications. 1172 * @param applicationContextClass the context class to set 1173 */ 1174 public void setApplicationContextClass( 1175 Class<? extends ConfigurableApplicationContext> applicationContextClass) { 1176 this.applicationContextClass = applicationContextClass; 1177 this.webApplicationType = WebApplicationType 1178 .deduceFromApplicationContext(applicationContextClass); 1179 } 1180 1181 /** 1182 * Sets the {@link ApplicationContextInitializer} that will be applied to the Spring 1183 * {@link ApplicationContext}. 1184 * @param initializers the initializers to set 1185 */ 1186 public void setInitializers( 1187 Collection<? extends ApplicationContextInitializer<?>> initializers) { 1188 this.initializers = new ArrayList<>(); 1189 this.initializers.addAll(initializers); 1190 } 1191 1192 /** 1193 * Add {@link ApplicationContextInitializer}s to be applied to the Spring 1194 * {@link ApplicationContext}. 1195 * @param initializers the initializers to add 1196 */ 1197 public void addInitializers(ApplicationContextInitializer<?>... initializers) { 1198 this.initializers.addAll(Arrays.asList(initializers)); 1199 } 1200 1201 /** 1202 * Returns read-only ordered Set of the {@link ApplicationContextInitializer}s that 1203 * will be applied to the Spring {@link ApplicationContext}. 1204 * @return the initializers 1205 */ 1206 public Set<ApplicationContextInitializer<?>> getInitializers() { 1207 return asUnmodifiableOrderedSet(this.initializers); 1208 } 1209 1210 /** 1211 * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication 1212 * and registered with the {@link ApplicationContext}. 1213 * @param listeners the listeners to set 1214 */ 1215 public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { 1216 this.listeners = new ArrayList<>(); 1217 this.listeners.addAll(listeners); 1218 } 1219 1220 /** 1221 * Add {@link ApplicationListener}s to be applied to the SpringApplication and 1222 * registered with the {@link ApplicationContext}. 1223 * @param listeners the listeners to add 1224 */ 1225 public void addListeners(ApplicationListener<?>... listeners) { 1226 this.listeners.addAll(Arrays.asList(listeners)); 1227 } 1228 1229 /** 1230 * Returns read-only ordered Set of the {@link ApplicationListener}s that will be 1231 * applied to the SpringApplication and registered with the {@link ApplicationContext} 1232 * . 1233 * @return the listeners 1234 */ 1235 public Set<ApplicationListener<?>> getListeners() { 1236 return asUnmodifiableOrderedSet(this.listeners); 1237 } 1238 1239 /** 1240 * Static helper that can be used to run a {@link SpringApplication} from the 1241 * specified source using default settings. 1242 * @param primarySource the primary source to load 1243 * @param args the application arguments (usually passed from a Java main method) 1244 * @return the running {@link ApplicationContext} 1245 */ 1246 public static ConfigurableApplicationContext run(Class<?> primarySource, 1247 String... args) { 1248 return run(new Class<?>[] { primarySource }, args); 1249 } 1250 1251 /** 1252 * Static helper that can be used to run a {@link SpringApplication} from the 1253 * specified sources using default settings and user supplied arguments. 1254 * @param primarySources the primary sources to load 1255 * @param args the application arguments (usually passed from a Java main method) 1256 * @return the running {@link ApplicationContext} 1257 */ 1258 public static ConfigurableApplicationContext run(Class<?>[] primarySources, 1259 String[] args) { 1260 return new SpringApplication(primarySources).run(args); 1261 } 1262 1263 /** 1264 * A basic main that can be used to launch an application. This method is useful when 1265 * application sources are defined via a {@literal --spring.main.sources} command line 1266 * argument. 1267 * <p> 1268 * Most developers will want to define their own main method and call the 1269 * {@link #run(Class, String...) run} method instead. 1270 * @param args command line arguments 1271 * @throws Exception if the application cannot be started 1272 * @see SpringApplication#run(Class[], String[]) 1273 * @see SpringApplication#run(Class, String...) 1274 */ 1275 public static void main(String[] args) throws Exception { 1276 SpringApplication.run(new Class<?>[0], args); 1277 } 1278 1279 /** 1280 * Static helper that can be used to exit a {@link SpringApplication} and obtain a 1281 * code indicating success (0) or otherwise. Does not throw exceptions but should 1282 * print stack traces of any encountered. Applies the specified 1283 * {@link ExitCodeGenerator} in addition to any Spring beans that implement 1284 * {@link ExitCodeGenerator}. In the case of multiple exit codes the highest value 1285 * will be used (or if all values are negative, the lowest value will be used) 1286 * @param context the context to close if possible 1287 * @param exitCodeGenerators exist code generators 1288 * @return the outcome (0 if successful) 1289 */ 1290 public static int exit(ApplicationContext context, 1291 ExitCodeGenerator... exitCodeGenerators) { 1292 Assert.notNull(context, "Context must not be null"); 1293 int exitCode = 0; 1294 try { 1295 try { 1296 ExitCodeGenerators generators = new ExitCodeGenerators(); 1297 Collection<ExitCodeGenerator> beans = context 1298 .getBeansOfType(ExitCodeGenerator.class).values(); 1299 generators.addAll(exitCodeGenerators); 1300 generators.addAll(beans); 1301 exitCode = generators.getExitCode(); 1302 if (exitCode != 0) { 1303 context.publishEvent(new ExitCodeEvent(context, exitCode)); 1304 } 1305 } 1306 finally { 1307 close(context); 1308 } 1309 } 1310 catch (Exception ex) { 1311 ex.printStackTrace(); 1312 exitCode = (exitCode != 0) ? exitCode : 1; 1313 } 1314 return exitCode; 1315 } 1316 1317 private static void close(ApplicationContext context) { 1318 if (context instanceof ConfigurableApplicationContext) { 1319 ConfigurableApplicationContext closable = (ConfigurableApplicationContext) context; 1320 closable.close(); 1321 } 1322 } 1323 1324 private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) { 1325 List<E> list = new ArrayList<>(elements); 1326 list.sort(AnnotationAwareOrderComparator.INSTANCE); 1327 return new LinkedHashSet<>(list); 1328 } 1329 1330}