spring boot源码分析之一《启动流程源码分析》
本篇主要分析Spring boot 启动开始的过程和怎么解析注入我们自己定义的类
1.首先从自定义的驱动类入口进行
1 package com.example.demo; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 6 7 import com.example.demo.config.MainConfig; 8 9 @SpringBootApplication 10 public class ParseSpringApplication { 11 12 public static void main(String[] args) { 13 SpringApplication.run(ParseSpringApplication.class, args); 14 } 15 16 }
(1)在main主函数中调用spring-boot.jar包中的SpringApplication的静态run方法,其中使用自定义类的class作为参数
(2)根据调用栈进入run方法,通过将自定义类封装成Class数组调用重载方法run(Class<?>[] primarySources, String[] args)

(3) 在重载方法里面实例化SpringApplication类并调用public ConfigurableApplicationContext run(String... args)方法,其中run方法内面是主要的初始化和环境准备过程
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置webApplication类型为Servlet this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
其中通过getSpringFactoriesInstances方法获取所有的ApplicationContext初始化器;
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
使用SpringFactoriesLoader类spring工厂加载器类在指定的jar包中,搜索META-INF/spring.factories属性文件,并遍历所有属性并对初始化器进行获取;
/** * Load the fully qualified class names of factory implementations of the * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given * class loader. * @param factoryClass the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading resources; can be * {@code null} to use the default * @throws IllegalArgumentException if an error occurs while loading factory names * @see #loadFactories */ public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { 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 factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
其中通过Launcher$AppClassLoader的getResources(FACTORIES_RESOURCE_LOCATION)获取具有属性文件META-INF/spring.factories的jar包,将获取的属性值保存到Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>()中,cache中缓存的为

到这里分别初始化了SpringApplication中的initializers和listeners,其中deduceMainApplicationClass寻找应用程序的main入口类的class
(4)回到SpringApplication中的具体ConfigurableApplicationContext run(String... args)实现的方法。
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); 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; }
主要的步骤为:
1)创建ApplicationContext实例
2)注册一个CommandLinePropertySource命令行参数作为Spring的属性
3)刷新ApplicationContext,加载所有的单例bean
4)触发运行所有的CommandLineRunner的beans
浙公网安备 33010602011771号