个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈
SpringBoot自启动源码分析
项目启动代码
public static void main(String[] args) {
// springboot项目启动方式
SpringApplication.run(DemoApplication.class, args);
}
springApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
// 调用run方法
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 最终创建了一个SpringApplication对象
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// 必须传一个类
Assert.notNull(primarySources, "PrimarySources must not be null");
// 用一个Set集合存储我们的启动类
this.primarySources = new LinkedHashSet<>(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
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 这个方法重点
// 这个方法里会加载spring.factories文件里的东西
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 初始化监听器的时候就会直接取了
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 扫描我们类路径下META-INF/spring.factories文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
// 添加到map中
// 这里map的映射关系是 接口-> 实现类
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
// 这个方法进行实例化
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
// 获取该类加载器下对应的实现类
// 我们可以手动实现ApplicationInitialzer和Listener
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
通过查看源码可知,如果要加载自己的类也可以通过自己建一个/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方法了
public ConfigurableApplicationContext run(String... args) {
// 秒表
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听,SpringApplicationRunListeners
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 配置环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印图标
Banner printedBanner = printBanner(environment);
// 默认加载AnnotationConfigServletWebServerApplicationContext.class
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 这里面调用了applyInitializers()方法 执行了 ApplicationContextInitialzer的initialize()方法
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;
}
springboot配置
附录
1.WebApplicationType枚举
public enum WebApplicationType {
/**
* 该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
*/
NONE,
/**
* 该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。
*/
SERVLET,
/**
* 该应用程序应作为反应式Web应用程序运行,并应启动嵌入式反应式Web服务器。
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
}
2.SpringApplicationBanner
通过类字段可以看到yml文件中配置banner的路径,以及将我们要展示的banner的文件名字取为banner.txt
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
// 指定Banner.Mode的方式
enum Mode {
/**
* Disable printing of the banner.
*/
OFF,
/**
* Print the banner to System.out.
*/
CONSOLE,
/**
* Print the banner to the log file.
*/
LOG
}
附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读/书籍书单大全:
(点击右侧 即可打开个人博客内有干货):技术干货小栈
=====>>①【Java大牛带你入门到进阶之路】<<====
=====>>②【算法数据结构+acm大牛带你入门到进阶之路】<<===
=====>>③【数据库大牛带你入门到进阶之路】<<=====
=====>>④【Web前端大牛带你入门到进阶之路】<<====
=====>>⑤【机器学习和python大牛带你入门到进阶之路】<<====
=====>>⑥【架构师大牛带你入门到进阶之路】<<=====
=====>>⑦【C++大牛带你入门到进阶之路】<<====
=====>>⑧【ios大牛带你入门到进阶之路】<<====
=====>>⑨【Web安全大牛带你入门到进阶之路】<<=====
=====>>⑩【Linux和操作系统大牛带你入门到进阶之路】<<=====天下没有不劳而获的果实,望各位年轻的朋友,想学技术的朋友,在决心扎入技术道路的路上披荆斩棘,把书弄懂了,再去敲代码,把原理弄懂了,再去实践,将会带给你的人生,你的工作,你的未来一个美梦。
本文深入剖析SpringBoot项目的自启动过程,从main方法入口开始,详细解读SpringApplication的run方法,包括如何加载和实例化SpringFactories,以及整个应用上下文的初始化流程。
1661

被折叠的 条评论
为什么被折叠?



