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 Loaderscom.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