SpringBoot的启动流程分析
Posted wanghaoyang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot的启动流程分析相关的知识,希望对你有一定的参考价值。
通过分析我们可以找到 org.springframework.boot.SpringApplication 中如下,
public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
可以看出,SpringBoot的启动分两步 1:实例化一个SpringApplication对象 2:run
初始化的方法如下:
private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); //设置当前为web环境 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
deduceWebEnvironment()方法会判断包路径下是否存在 "javax.servlet.Servlet" 和 "org.springframework.web.context.ConfigurableWebApplicationContext",如果存在,则设置当前环境为Web环境,源码如下
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
setInitializers()方法用来设置SpringApplication启动时需要的类
setListeners()用来设置SpringApplication启动时需要的监听器,
这两个方法都用到了getSpringFactoriesInstances()方法,那我们来分析一下这个方法的相关源码
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
通过上面的代码我们可以看出,getSpringFactoriesInstances() 方法会从 "META-INF/spring.factories" 这个文件中读取需要的类型实例化后注入到SpringApplication中,我们截取springboot中"META-INF/spring.factories" 中 ApplicationContextInitializer.class和
ApplicationListener.class类型的类如下
# Application Context Initializers org.springframework.context.ApplicationContextInitializer=org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,org.springframework.boot.context.ContextIdApplicationContextInitializer,org.springframework.boot.context.config.DelegatingApplicationContextInitializer,org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
# Application Listeners org.springframework.context.ApplicationListener=org.springframework.boot.ClearCachesApplicationListener,org.springframework.boot.builder.ParentContextCloserApplicationListener,org.springframework.boot.context.FileEncodingApplicationListener,org.springframework.boot.context.config.AnsiOutputApplicationListener,org.springframework.boot.context.config.ConfigFileApplicationListener,org.springframework.boot.context.config.DelegatingApplicationListener,org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,org.springframework.boot.logging.ClasspathLoggingApplicationListener,org.springframework.boot.logging.LoggingApplicationListener
deduceMainApplicationClass()将启动类设置为我们自己定义的启动类,它是用过读取程序的栈,并分析栈的方法是否为main来判断的,源码如下
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
那么以上就是SpringApplication初始化完成的事情,限于篇幅,我们在下一章介绍run的过程
以上是关于SpringBoot的启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章