SpringBoot启动源码分析

编程

SpringBoot自启动源码分析

项目启动代码

publicstaticvoidmain(String[] args) {

  // springboot项目启动方式

       SpringApplication.run(DemoApplication.class, args);

  }

springApplication.java

publicstaticConfigurableApplicationContextrun(Class<?>primarySource, String... args) {

  // 调用run方法

returnrun(newClass<?>[] { primarySource }, args);

}

publicstaticConfigurableApplicationContextrun(Class<?>[] primarySources, String[] args) {

       // 最终创建了一个SpringApplication对象

returnnewSpringApplication(primarySources).run(args);

}

publicSpringApplication(ResourceLoaderresourceLoader, Class<?>... primarySources) {

this.resourceLoader=resourceLoader;

  // 必须传一个类

Assert.notNull(primarySources, "PrimarySources must not be null");

  // 用一个Set集合存储我们的启动类

this.primarySources=newLinkedHashSet<>(Arrays.asList(primarySources));

  // webApplication的枚举类型

  // 主要判断我们启动的类型是什么,一般我们启动的是servlet

this.webApplicationType=WebApplicationType.deduceFromClasspath();

  // 初始化一些应用上下文

  // 加载springboot包下的/META-INF/spring.facotries中的8个类

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

  // 初始化监听器

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

  // 通过类加载器启动我们的类

  // Class.forName()

this.mainApplicationClass=deduceMainApplicationClass();

}

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

我们来看看初始化应用上下文中做了什么

private<T>Collection<T>getSpringFactoriesInstances(Class<T>type, Class<?>[] parameterTypes, Object... args) {

  // 获取类加载器,我们传入的ApplicationContextInitializer.class

ClassLoaderclassLoader=getClassLoader();

// Use names and ensure unique to protect against duplicates

  // 这个方法重点

  // 这个方法里会加载spring.factories文件里的东西

Set<String>names=newLinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

  // 实例化对象

List<T>instances=createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

AnnotationAwareOrderComparator.sort(instances);

returninstances;

}

SpringFactoriesLoader.loadFactoryNames(type, classLoader)

privatestaticMap<String, List<String>>loadSpringFactories(@NullableClassLoaderclassLoader) {

  // 初始化监听器的时候就会直接取了

MultiValueMap<String, String>result=cache.get(classLoader);

if (result!=null) {

returnresult;

}

try {

           // 扫描我们类路径下META-INF/spring.factories文件

Enumeration<URL>urls= (classLoader!=null?

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

result=newLinkedMultiValueMap<>();

while (urls.hasMoreElements()) {

URLurl=urls.nextElement();

UrlResourceresource=newUrlResource(url);

Propertiesproperties=PropertiesLoaderUtils.loadProperties(resource);

for (Map.Entry<?, ?>entry : properties.entrySet()) {

StringfactoryTypeName= ((String) entry.getKey()).trim();

for (StringfactoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {

                       // 添加到map中

                       // 这里map的映射关系是 接口-> 实现类

result.add(factoryTypeName, factoryImplementationName.trim());

}

}

}

cache.put(classLoader, result);

returnresult;

}

catch (IOExceptionex) {

thrownewIllegalArgumentException("Unable to load factories from location ["+

FACTORIES_RESOURCE_LOCATION+"]", ex);

}

}

// 这个方法进行实例化

private<T>List<T>createSpringFactoriesInstances(Class<T>type, Class<?>[] parameterTypes,

ClassLoaderclassLoader, Object[] args, Set<String>names) {

List<T>instances=newArrayList<>(names.size());

  // 获取该类加载器下对应的实现类

  // 我们可以手动实现ApplicationInitialzer和Listener

for (Stringname : names) {

try {

Class<?>instanceClass=ClassUtils.forName(name, classLoader);

Assert.isAssignable(type, instanceClass);

Constructor<?>constructor=instanceClass.getDeclaredConstructor(parameterTypes);

Tinstance= (T) BeanUtils.instantiateClass(constructor, args);

instances.add(instance);

}

catch (Throwableex) {

thrownewIllegalArgumentException("Cannot instantiate "+type+" : "+name, ex);

}

}

returninstances;

}

通过查看源码可知,如果要加载自己的类也可以通过自己建一个/META-INF/spring.factories文件

# PropertySource Loaders

com.example.demo.service.NewService=

com.example.demo.service.NewServiceImpl

org.springframework.context.ApplicationContextInitializer=

com.example.demo.service.TextApplicationInitializer

springApplication对象建好以后就是调用我们的run方法了

publicConfigurableApplicationContextrun(String... args) {

  // 秒表

StopWatchstopWatch=newStopWatch();

stopWatch.start();

ConfigurableApplicationContextcontext=null;

Collection<SpringBootExceptionReporter>exceptionReporters=newArrayList<>();

configureHeadlessProperty();

SpringApplicationRunListenerslisteners=getRunListeners(args);

  // 启动监听,SpringApplicationRunListeners

listeners.starting();

try {

ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(args);

           // 配置环境

ConfigurableEnvironmentenvironment=prepareEnvironment(listeners, applicationArguments);

configureIgnoreBeanInfo(environment);

           // 打印图标

BannerprintedBanner=printBanner(environment);

           // 默认加载AnnotationConfigServletWebServerApplicationContext.class

context=createApplicationContext();

exceptionReporters=getSpringFactoriesInstances(SpringBootExceptionReporter.class,

newClass[] { ConfigurableApplicationContext.class }, context);

           // 这里面调用了applyInitializers()方法 执行了 ApplicationContextInitialzer的initialize()方法

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

refreshContext(context);

afterRefresh(context, applicationArguments);

stopWatch.stop();

if (this.logStartupInfo) {

newStartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);

}

listeners.started(context);

callRunners(context, applicationArguments);

}

catch (Throwableex) {

handleRunFailure(context, ex, exceptionReporters, listeners);

thrownewIllegalStateException(ex);

}

try {

listeners.running(context);

}

catch (Throwableex) {

handleRunFailure(context, ex, exceptionReporters, null);

thrownewIllegalStateException(ex);

}

returncontext;

}

springboot配置

附录

1.WebApplicationType枚举

publicenumWebApplicationType {

/**

* 该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。

*/

NONE,

/**

* 该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。

*/

SERVLET,

/**

* 该应用程序应作为反应式Web应用程序运行,并应启动嵌入式反应式Web服务器。

*/

REACTIVE;

privatestaticfinalString[] SERVLET_INDICATOR_CLASSES= { "javax.servlet.Servlet",

"org.springframework.web.context.ConfigurableWebApplicationContext" };

privatestaticfinalStringWEBMVC_INDICATOR_CLASS="org.springframework.web.servlet.DispatcherServlet";

privatestaticfinalStringWEBFLUX_INDICATOR_CLASS="org.springframework.web.reactive.DispatcherHandler";

privatestaticfinalStringJERSEY_INDICATOR_CLASS="org.glassfish.jersey.servlet.ServletContainer";

privatestaticfinalStringSERVLET_APPLICATION_CONTEXT_CLASS="org.springframework.web.context.WebApplicationContext";

privatestaticfinalStringREACTIVE_APPLICATION_CONTEXT_CLASS="org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

}

2.SpringApplicationBanner

通过类字段可以看到yml文件中配置banner的路径,以及将我们要展示的banner的文件名字取为banner.txt

staticfinalStringBANNER_LOCATION_PROPERTY="spring.banner.location";

staticfinalStringBANNER_IMAGE_LOCATION_PROPERTY="spring.banner.image.location";

staticfinalStringDEFAULT_BANNER_LOCATION="banner.txt";

staticfinalString[] IMAGE_EXTENSION= { "gif", "jpg", "png" };

// 指定Banner.Mode的方式

enumMode {

/**

* Disable printing of the banner.

*/

OFF,

/**

* Print the banner to System.out.

*/

CONSOLE,

/**

* Print the banner to the log file.

*/

LOG

}

以上是 SpringBoot启动源码分析 的全部内容, 来源链接: utcz.com/z/514525.html

回到顶部