----------------------------------------------------------------------------------------------------------------------

Spring Boot启动过程主要方法++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

SpringApplication()

this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

SpringApplication.run()

createBootstrapContext

this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,

listeners.starting

prepareEnvironment

printBanner

context = createApplicationContext();

prepareContext(bootstrapContext, context,

applyInitializers(context);

listeners.contextPrepared

bootstrapContext.close

Set<Object> sources = getAllSources();

load(context, sources.toArray(new Object[0]));

BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);

this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

new RootBeanDefinition(ConfigurationClassPostProcessor.class);

new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);

new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);

new RootBeanDefinition(EventListenerMethodProcessor.class);

new RootBeanDefinition(DefaultEventListenerFactory.class);

this.scanner = new ClassPathBeanDefinitionScanner(registry);

loader.setBeanNameGenerator(this.beanNameGenerator);

loader.setResourceLoader(this.resourceLoader);

loader.setEnvironment(this.environment);

loader.load();//此行注册了主类bean

listeners.contextLoaded

refreshContext(context);

postProcessBeanFactory

this.scanner.scan

this.reader.register

invokeBeanFactoryPostProcessors //ConfigurationClassPostProcessor回调函数解析出了注解bean,生成了beandefinition

registerBeanPostProcessors

initApplicationEventMulticaster

registerListeners

getApplicationEventMulticaster().addApplicationListener(listener);

getApplicationEventMulticaster().multicastEvent(earlyEvent);

finishBeanFactoryInitialization

beanFactory.preInstantiateSingletons();

finishRefresh

publishEvent(new ContextRefreshedEvent(this));

listeners.started

callRunners

感觉BootstrapContext存在的作用只是给SpringApplicationRunListener提供事件广播支持。而SpringApplicationRunListener用于记录Application整个启动过程。

prepareContext 方法中,SpringBoot会把主类注册到Spring容器中,为什么要这么做昵?主类上的注解 @SpringBootApplication 需要 ConfigurationClassPostProcessor 解析,才能发挥@Import@ComponentScan的作用,想要 ConfigurationClassPostProcessor 处理主类的前提是主类的BeanDefinition需要在Spring容器中。

invokeBeanFactoryPostProcessors回调processor,而ConfigurationClassPostProcessor回调函数调用processConfigBeanDefinitions()函数创建了ConfigurationClassParser对象,执行parse解析出了注解bean,生成了beandefinition

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

package org.springframework.context.annotation;

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,

BeanRegistrationAotProcessor, BeanFactoryInitializationAotProcessor, PriorityOrdered,

ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {//这两个回调函数都会调用processConfigBeanDefinitions

processConfigBeanDefinitions(registry);

postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {//这两个回调函数都会调用processConfigBeanDefinitions

processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

。。。。。。

ConfigurationClassParser parser = new ConfigurationClassParser(

this.metadataReaderFactory, this.problemReporter, this.environment,

this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);

Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

do {

StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");

parser.parse(candidates);//此行解析出了注解bean,生成了beandefinition

----------------------------------------------------------------------------------------------------------------------




剩下的内容是最开始的时候写的,比较菜。。。。。。。

RTFSC懂得都懂。。。

呐,框子就这6行包括2行大括号。。。。。。

@SpringBootApplication
public class LearnSpringbootApplication {
public static void main(String[] args) {
SpringApplication.
run(LearnSpringbootApplication.class, args);
}
}

-----------------------------------------------------------------------------------------------------------------------

下边是@SpringBootApplication注解的源码注释。

Indicates a configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan.

-----------------------------------------------------------------------------------------------------------------------

下边是SpringApplication类的源码注释。大意是:SpringApplication类可以用在main函数中以启动一个Spring应用,它做的操作有4步:1创建ApplicationContext实例,2读取命令行参数作为properties3刷新context载入所有单例bean4启动所有CommandLineRunner beans

* Class that can be used to bootstrap and launch a Spring application from a Java main
* method. By default class will perform the following steps to bootstrap your
* application:
*
<ul>
* <li>Create an appropriate {@link ApplicationContext} instance (depending on your
* classpath)
</li>
* <li>Register a {@link CommandLinePropertySource} to expose command line arguments as
* Spring properties
</li>
* <li>Refresh the application context, loading all singleton beans</li>
* <li>Trigger any {@link CommandLineRunner} beans</li>
* </ul>
* In most circumstances the static {
@link #run(Class, String[])} method can be called
* directly from your {
@literal main} method to bootstrap your application:
*
<pre class="code">
* &#064;Configuration
*
&#064;EnableAutoConfiguration
* public class MyApplication {
* // ... Bean definitions
* public static void main(String[] args) {
* SpringApplication.run(MyApplication.class, args);
* }
* }
*
</pre>
*
<p>
* For more advanced configuration a {@link SpringApplication} instance can be created and
* customized before being run:
*
<pre class="code">
* public static void main(String[] args) {
* SpringApplication application = new SpringApplication(MyApplication.class);
* // ... customize application settings here
* application.run(args)
* }
*
</pre>
* {
@link SpringApplication}s can read beans from a variety of different sources. It is
* generally recommended that a single {
@code @Configuration} class is used to bootstrap
* your application, however, you may also set {
@link #getSources() sources} from:
*
<ul>
* <li>The fully qualified class name to be loaded by
* {
@link AnnotatedBeanDefinitionReader}</li>
* <li>The location of an XML resource to be loaded by {@link XmlBeanDefinitionReader}, or
* a groovy script to be loaded by {
@link GroovyBeanDefinitionReader}</li>
* <li>The name of a package to be scanned by {@link ClassPathBeanDefinitionScanner}</li>
* </ul>
* Configuration properties are also bound to the {
@link SpringApplication}. This makes it
* possible to set {
@link SpringApplication} properties dynamically, like additional
* sources ("spring.main.sources" - a CSV list) the flag to indicate a web environment
* ("spring.main.web-application-type=none") or the flag to switch off the banner
* ("spring.main.banner-mode=off").

先看静态run方法,有2个,汗。。。:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}

查资料发现原来可以有多个主类,多模块运行。。。。。。

仔细看第二个run的参数是primarySources,多个s。。。是个数组。一般单模块就是第1run调用第2run;而多模块就是直接调用第二个run。。。。。。

第二个静态runnew一个 SpringApplication 对象,然后加args调用对象的run。。。。。。

先说对象创建:又有2构造函数。。。跟上边一样,第1个是个壳子,用于没有传入resourceLoader情况,这是默认情况。。。

public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
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 = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.
class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.
class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.
class));
this.mainApplicationClass = deduceMainApplicationClass();
}

WebApplicationType.deduceFromClasspath()根据Classpath下是否有相应的类来推断容器类型,默认是servlet


接着看 getSpringFactoriesInstances()方法:其调用了SpringFactoriesLoader类的静态方法forDefaultResourceLocation来生成SpringFactoriesLoader对象并调用其load方法。

先看静态方法forDefaultResourceLocation是怎么生成SpringFactoriesLoader对象的,传入参数是getClassLoader()这实际是Thread.currentThread().getContextClassLoader()也就是ApplicationClassLoader。一通Map代码。。。。。。根据传入的META-INF/spring.factoriesApplicationClassLoader最后new了个SpringFactoriesLoader对象返回。。。而new构造函数实际就传了2个成员变量值,一个是classloader,一个是loadFactoriesResource 方法生成的Map。。。loadFactoriesResource 方法把本程序及依赖库的META-INF/spring.factories内容转换成了Map

SpringFactoriesLoader对象的load方法先获取factory名称然后实例化通过loadFactoryNames ()instantiateFactory()

private <T> List<T> getSpringFactoriesInstances(Class<T> type, ArgumentResolver argumentResolver) {
return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver);
}
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader.getClassLoader();
}
return ClassUtils.getDefaultClassLoader();
}
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl =
null;
try {
cl = Thread.
currentThread().getContextClassLoader();
}
。。。。。。。。。。。。。。。。
}
public class SpringFactoriesLoader {
/**
* The location to look for factories.
*
<p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final FailureHandler THROWING_FAILURE_HANDLER = FailureHandler.throwing();
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
static final Map<ClassLoader, Map<String, SpringFactoriesLoader>> cache = new ConcurrentReferenceHashMap<>();
@Nullable
private final ClassLoader classLoader;
private final Map<String, List<String>> factories;
public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
Assert.hasText(resourceLocation,
"'resourceLocation' must not be empty");
ClassLoader resourceClassLoader = (classLoader !=
null ? classLoader :
SpringFactoriesLoader.
class.getClassLoader());
Map<String, SpringFactoriesLoader> loaders =
cache.computeIfAbsent(
resourceClassLoader, key ->
new ConcurrentReferenceHashMap<>());
return loaders.computeIfAbsent(resourceLocation, key ->
new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}
protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
Map<String, List<String>> result =
new LinkedHashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(resourceLocation);
while (urls.hasMoreElements()) {
UrlResource resource =
new UrlResource(urls.nextElement());
Properties properties = PropertiesLoaderUtils.
loadProperties(resource);
properties.forEach((name, value) -> {
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);
List<String> implementations =
result.computeIfAbsent(((String) name).trim(),
key ->
new ArrayList<>(factoryImplementationNames.length));
Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);
});
}
result.replaceAll(SpringFactoriesLoader::
toDistinctUnmodifiableList);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);
}
return Collections.unmodifiableMap(result);
}
protected SpringFactoriesLoader(@Nullable ClassLoader classLoader, Map<String, List<String>> factories) {
this.classLoader = classLoader;
this.factories = factories;
}
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,
@Nullable FailureHandler failureHandler) {
Assert.notNull(factoryType,
"'factoryType' must not be null");
List<String> implementationNames =
loadFactoryNames(factoryType);
logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
List<
T> result = new ArrayList<>(implementationNames.size());
FailureHandler failureHandlerToUse = (failureHandler !=
null) ? failureHandler : THROWING_FAILURE_HANDLER;
for (String implementationName : implementationNames) {
T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
if (factory != null) {
result.add(factory);
}
}
AnnotationAwareOrderComparator.sort(result);
return result;
}

再来看SpringFactoriesLoader对象的loadFactoryNames ()instantiateFactory()

先说loadFactoryNames(factoryType)方法:它返回了一个SpringFactoriesLoader成员变量this.factories这个mapvalue。这个成员变量是在类构造函数中初始化的,就是上边那个loadFactoriesResource处理各个包META-INF/spring.factories得到的。。。。。而instantiateFactory()方法则会通过 FactoryInstantiator.forClass()给每个factory创建一个 factoryInstantiator调用 constructor.newInstance(args)创建出一个对象。

private List<String> loadFactoryNames(Class<?> factoryType) {
return this.factories.getOrDefault(factoryType.getName(), Collections.emptyList());
}
protected <T> T instantiateFactory(String implementationName, Class<T> type,
@Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) {

try {
Class<?> factoryImplementationClass = ClassUtils.
forName(implementationName, this.classLoader);
Assert.
isTrue(type.isAssignableFrom(factoryImplementationClass), () ->
"Class [%s] is not assignable to factory type [%s]".formatted(implementationName, type.getName()));
FactoryInstantiator<
T> factoryInstantiator = FactoryInstantiator.forClass(factoryImplementationClass);
return factoryInstantiator.instantiate(argumentResolver);
}
catch (Throwable ex) {
failureHandler.handleFailure(type, implementationName, ex);
return null;
}
}
T instantiate(@Nullable ArgumentResolver argumentResolver) throws Exception {
Object[] args = resolveArgs(argumentResolver);
if (isKotlinType(this.constructor.getDeclaringClass())) {
return KotlinDelegate.instantiate(this.constructor, args);
}
return this.constructor.newInstance(args);
}

汗!!!也就是说SpringApplication对象的getSpringFactoriesInstances()方法绕了一圈子就是

1.META-INF/spring.factories文件中根据传入的类.class得对应的map(一个List)。。。

2.遍历这个list调用SpringFactoriesLoader对象的instantiateFactory()方法实例化这些factories


接着看getSpringFactoriesInstances(BootstrapRegistryInitializer.class)获取META-INF/spring.factories 中配置 key org.springframework.boot.BootstrapRegistryInitializer 的数据实例化相应的factoryBootstrapRegistryInitializerSpring Boot框架中一个非常重要的类,它允许开发者通过实现ApplicationContextInitializer接口来编写自定义的Bean初始化器。通过向ApplicationContext注册这些自定义的初始化器,开发者可以在Spring Boot应用启动时执行自定义的逻辑,实现对Bean的自定义初始化。。。。。。太深入了,先不管它继续往下。。。。。。


接着是ApplicationContextInitializer,这个

package org.springframework.context;
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}

initialize方法的形参类型是ConfigurableApplicationContext,因此可以认为ApplicationContextInitializer实际上是Spring容器初始化前ConfigurableApplicationContext的回调接口,可以对上下文环境作一些操作,如运行环境属性注册、激活配置文件等。可以实际试一下:

class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.
out.println("----------MyApplicationContextInitializer---------------");
Map<String, Object> systemProperties = applicationContext.getEnvironment().getSystemProperties();
System.out.println("---------------start------------------");
systemProperties.forEach((key,value)->{
System.out.println("key:"+key+",value:"+value);
}); System.out.println("---------------end------------------");
}
}

接着是ApplicationListener,先过。。。。。。


接着是this.mainApplicationClass = deduceMainApplicationClass();deduceMainApplicationClass 主要是通过StackWalker来找到main方法,main方法所在的class就是启动类。不明觉厉。。。。。。

private Class<?> deduceMainApplicationClass() {
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
.walk(
this::findMainClass)
.orElse(
null);
}

SpringApplication 对象终于构造完了。。。。。。

接着是其run(args)方法

public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.
create();
if (this.registerShutdownHook) {
SpringApplication.
shutdownHook.enableShutdownHookAddition();
}
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context =
null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext,
this.mainApplicationClass);
try {
ApplicationArguments applicationArguments =
new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(
this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;
}

东西比较多,先看个顺序了解全貌,不深入。。。

1个是Startup类,貌似主要是计时。。。。。。

然后是shutdownHook,貌似是跟停应用有关。。。

然后是createBootstrapContext()方法

private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext =
new DefaultBootstrapContext();
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}

有必要了解一下DefaultBootstrapContext里边啥玩意儿内容。。。。。。两个Map一个enents。。。

public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
private final Map<Class<?>, InstanceSupplier<?>> instanceSuppliers = new HashMap<>();
private final Map<Class<?>, Object> instances = new HashMap<>();
private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();

跟前边构造方法中提到的BootstrapRegistryInitializer有关。。。

然后新建一个局部变量ConfigurableApplicationContext context = null;,这是Spring应用的IoC容器,贯穿整个方法。

然后configureHeadlessProperty()设置无头模式,默认是true。。。

然后是 SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting(bootstrapContext, this.mainApplicationClass);

这应该是配置监听器,前边构造函数也涉及了这个。

然后是程序运行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

然后是环境ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

然后是打印Banner printedBanner = printBanner(environment);

创建IoC容器context = createApplicationContext();比较复杂,先过。。。。。。

protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}

然后是context.(this.applicationStartup);不知道干啥的,看似不太重要。。。先过

然后三连。。。应该比较复杂,有空再研究,先把run走完。。。

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

refreshContext(context);

afterRefresh(context, applicationArguments);

然后是startup.started();发布SpringBoot程序已启动的事件。。。。。。

然后是listeners.started(context, startup.timeTakenToStarted());通知所有listeners

然后是callRunners(context, applicationArguments);调用ApplicationRunnerCommandLineRunner通知他们可以操作了。。。

终于。。。run()结束了

-------------------------------------------------------------------------------------------------------------------

为了更深入理解上边Springboot的源码,需要学习一些知识点和Springboot的一些手法和模式:

套壳方法:像两个run静态函数,外层参数用Class<?> primarySource内层参数用Class<?>[] primarySources,这样就有两种入口且实现相同的功能。重载方法。。。

Class<?>范型.class对象作为Class<?>范型传递:Class<?> primarySource,这个 primarySource接收实参YourApplication.class作为对象,其实这里Class<?>换作Class也行。问:在Java中,Class<?>Class的主要区别是什么?答:Class<?>Class在许多情况下都可以用来表示未知的类类型。但Class<?>被认为是一个通配符泛型,表示它是安全的,而Class通常被视为一个原始类型。使用Class<?>是更加类型安全的方式,因为它告诉编译器你明确地想表示一个未知的类型,而不是简单地忽略了泛型。不使用泛型时,反射创建类时需进行强转,若类型不符会抛出ClassCastException。而泛型Class则无需强转。

Classloader对象是从哪里获得资源的:ClassLoader对象的loadResources(“path”)函数会从本程序和所有依赖jar包的path中获取资源。path加“/”是从根目录开始,不加是从classpath开始。

Map<>对象的computeIfAbsent 方法:cache.computeIfAbsent(resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());完成的功能为:如果查到就返回,查不到则执行lambda表达式添加一条记录,用作登记式缓存。那么这个 ConcurrentReferenceHashMap到底有什么作用呢?ConcurrentReferenceHashMap 能指定所存放对象的引用级别默认情况下是软引用级别。(四种引用级别:强///虚)

接口的默认实现:神仙写法。。。。。。接口里边定义了一个static final名叫DEFAULT引用了一个new实现,汗!!!

public class SpringApplication {private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;......}

public interface ApplicationContextFactory {ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();......}

class DefaultApplicationContextFactory implements ApplicationContextFactory {...…}