本系列springboot源码都基于 2.2.5.RELEASE
入口类 1 2 3 4 5 6 7 8 @SpringBootApplication public class HelloWorldApplication { public static void main (String[] args) { SpringApplication.run(HelloWorldApplication.class, args); } }
一直跟进方法,这里将入口类当做参数,创建了一个SpringApplication
实例,并调用run(args)
方法。
1 2 3 public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return new SpringApplication (primarySources).run(args); }
继续跟进构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet <>(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); }
推断应用类型 deduceFromClasspath()
方法查找一些类是否存在来判断是reactive、还是servlet服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static WebApplicationType deduceFromClasspath () { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null )) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null )) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } 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" ;
设置初始化器 跟踪方法,最后调用的方法是getSpringFactoriesInstances
,传入的参数 type 是ApplicationContextInitializer.class
。
1 2 3 4 5 6 7 8 9 10 11 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet <>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
跟进loadFactoryNames()
方法,它将ApplicationContextInitializer.class
的名字作为 key 去从 map 中取出想要的名字。
1 2 3 4 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
继续跟进
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;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 factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { 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); } }
以 org.springframework.boot:spring-boot
包为例,它的META-INF/spring.factories
文件中包含以下内容:
1 2 3 4 5 6 7 8 org.springframework.context.ApplicationContextInitializer =\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
那么,这五个类名就会被读出来放到Set<String> names
中,并开始准备下面的实例化操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private <T> List<T> createSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList <>(names.size()); 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; }
设置监听器 我们跟进监听器,可以发现代码和上面设置初始化器的流程一模一样,这里入参变成了ApplicationListener.class
。再查看一下 org.springframework.boot:spring-boot
的META-INF/spring.factories
文件,包含以下内容::
1 2 3 4 5 6 7 8 9 10 11 12 13 org.springframework.context.ApplicationListener =\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ 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.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
这些监听器会贯穿springboot整个生命周期。至此,对SpringApplication实例的初始化过程就结束了。
run 方法 主要分为八个步骤,注释在代码中了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 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; }
第一步:获取并启动监听器 跟进方法,这里流程和获取初始化器、监听器一样,传入的 class 为SpringApplicationRunListener.class
1 2 3 4 5 private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class <?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners (logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this , args)); }
查看META-INF/spring.factories
文件相应的key-value,只有一个类。
1 2 3 org.springframework.boot.SpringApplicationRunListener =\ org.springframework.boot.context.event.EventPublishingRunListener
查看这个类的构造方法
1 2 3 4 5 6 7 8 public EventPublishingRunListener (SpringApplication application, String[] args) { this .application = application; this .args = args; this .initialMulticaster = new SimpleApplicationEventMulticaster (); for (ApplicationListener<?> listener : application.getListeners()) { this .initialMulticaster.addApplicationListener(listener); } }
我们在实例化EventPublishingRunListener的时候,是会将当前SpringApplication实例传入的,这个实例中含有我们一开始所有生成的监听器实例。构造器中new了一个SimpleApplicationEventMulticaster
广播器实例,并将application中的listener添加到广播器中,这里使用了一个内部类来存储所有监听器。
现在知道EventPublishingRunListener中有一个广播器SimpleApplicationEventMulticaster,这个广播器中又存放所有spring.factories中的监听器
发送开始启动事件 上一步我们通过 getRunListeners
获取的监听器为EventPublishingRunListener
,从名字可以看出是启动事件发布监听器,主要用来发布启动事件。
先看一下这个类所实现的接口,这个接口定义了springboot启动初始化过程中的状态。我们也可以添加自己的监听器,能够监听springboot启动时的各种状态,然后处理自己的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public interface SpringApplicationRunListener { default void starting () { } default void environmentPrepared (ConfigurableEnvironment environment) { } default void contextPrepared (ConfigurableApplicationContext context) { } default void contextLoaded (ConfigurableApplicationContext context) { } default void started (ConfigurableApplicationContext context) { } default void running (ConfigurableApplicationContext context) { } default void failed (ConfigurableApplicationContext context, Throwable exception) { } }
我们看一下第一个接口,广播器发送了一个ApplicationStartingEvent
事件,所有的事件都继承了SpringApplicationEvent
抽象类
1 2 3 4 @Override public void starting () { this .initialMulticaster.multicastEvent(new ApplicationStartingEvent (this .application, this .args)); }
跟进方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public void multicastEvent (final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null ) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
如何调用监听器呢,其实就是执行监听器接口的唯一一个方法onApplicationEvent(event)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void doInvokeListener (ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
第二步:准备环境 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { environment = new EnvironmentConverter (getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
跟进方法,先会判断是否有环境,如归没有的话就根据 webApplicationType 创建一个,如果是web服务的话,就会 new StandardServletEnvironment()
。注意:这里创建的ConfigurableEnvironment环境对象将一直被后面的流程使用
1 2 3 4 5 6 7 8 9 10 11 12 13 private ConfigurableEnvironment getOrCreateEnvironment () { if (this .environment != null ) { return this .environment; } switch (this .webApplicationType) { case SERVLET: return new StandardServletEnvironment (); case REACTIVE: return new StandardReactiveWebEnvironment (); default : return new StandardEnvironment (); } }
配置完环境后,发送环境已准备的事件,接下来我们看下**ConfigFileApplicationListener
**类,这个监听器非常核心,项目的yml文件都是由其内部类加载的。
1 2 3 4 5 6 7 8 9 @Override public void onApplicationEvent (ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } }
继续跟进,会调用onApplicationEnvironmentPreparedEvent
方法来处理ApplicationEnvironmentPreparedEvent
事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void onApplicationEnvironmentPreparedEvent (ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this ); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } } private void onApplicationEnvironmentPreparedEvent (ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this ); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } } List<EnvironmentPostProcessor> loadPostProcessors () { return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader()); }
首先loadPostProcessors()
还是会读取spring.factories
文件下对应的类名,将这些环境后处理器
添加到列表并执行后,ConfigFileApplicationListener
会执行它自己的postProcessEnvironment()
方法逻辑。
1 2 3 4 5 6 org.springframework.boot.env.EnvironmentPostProcessor =\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\ org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
不停地跟踪方法,发现最后 new 了一个Loader
对象并调用其load()
方法。
1 2 3 4 5 6 7 8 9 @Override public void postProcessEnvironment (ConfigurableEnvironment environment, SpringApplication application) { addPropertySources(environment, application.getResourceLoader()); } protected void addPropertySources (ConfigurableEnvironment environment, ResourceLoader resourceLoader) { RandomValuePropertySource.addToEnvironment(environment); new Loader (environment, resourceLoader).load(); }
查看这个内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { this .environment = environment; this .placeholdersResolver = new PropertySourcesPlaceholdersResolver (this .environment); this .resourceLoader = (resourceLoader != null ) ? resourceLoader : new DefaultResourceLoader (); this .propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); } void load () { FilteredPropertySource.apply(this .environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, (defaultProperties) -> { this .profiles = new LinkedList <>(); this .processedProfiles = new LinkedList <>(); this .activatedProfiles = false ; this .loaded = new LinkedHashMap <>(); initializeProfiles(); while (!this .profiles.isEmpty()) { Profile profile = this .profiles.poll(); if (isDefaultProfile(profile)) { addProfileToEnvironment(profile.getName()); } load(profile, this ::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false )); this .processedProfiles.add(profile); } load(null , this ::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true )); addLoadedPropertySources(); applyActiveProfiles(defaultProperties); }); } public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active" ;public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include" ;private void initializeProfiles () { this .profiles.add(null ); Set<Profile> activatedViaProperty = getProfilesFromProperty(ACTIVE_PROFILES_PROPERTY); Set<Profile> includedViaProperty = getProfilesFromProperty(INCLUDE_PROFILES_PROPERTY); List<Profile> otherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty); this .profiles.addAll(otherActiveProfiles); this .profiles.addAll(includedViaProperty); addActiveProfiles(activatedViaProperty); if (this .profiles.size() == 1 ) { for (String defaultProfileName : this .environment.getDefaultProfiles()) { Profile defaultProfile = new Profile (defaultProfileName, true ); this .profiles.add(defaultProfile); } } }
构造方法里初始了属性构造器,加载了PropertySourceLoader
类,查看spring.factories
1 2 3 4 org.springframework.boot.env.PropertySourceLoader =\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader
有两个类,分别看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class PropertiesPropertySourceLoader implements PropertySourceLoader { private static final String XML_FILE_EXTENSION = ".xml" ; @Override public String[] getFileExtensions() { return new String [] { "properties" , "xml" }; } @Override public List<PropertySource<?>> load(String name, Resource resource) throws IOException { Map<String, ?> properties = loadProperties(resource); if (properties.isEmpty()) { return Collections.emptyList(); } return Collections .singletonList(new OriginTrackedMapPropertySource (name, Collections.unmodifiableMap(properties), true )); } @SuppressWarnings({ "unchecked", "rawtypes" }) private Map<String, ?> loadProperties(Resource resource) throws IOException { String filename = resource.getFilename(); if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { return (Map) PropertiesLoaderUtils.loadProperties(resource); } return new OriginTrackedPropertiesLoader (resource).load(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class YamlPropertySourceLoader implements PropertySourceLoader { @Override public String[] getFileExtensions() { return new String [] { "yml" , "yaml" }; } @Override public List<PropertySource<?>> load(String name, Resource resource) throws IOException { if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml" , null )) { throw new IllegalStateException ( "Attempted to load " + name + " but snakeyaml was not found on the classpath" ); } List<Map<String, Object>> loaded = new OriginTrackedYamlLoader (resource).load(); if (loaded.isEmpty()) { return Collections.emptyList(); } List<PropertySource<?>> propertySources = new ArrayList <>(loaded.size()); for (int i = 0 ; i < loaded.size(); i++) { String documentNumber = (loaded.size() != 1 ) ? " (document #" + i + ")" : "" ; propertySources.add(new OriginTrackedMapPropertySource (name + documentNumber, Collections.unmodifiableMap(loaded.get(i)), true )); } return propertySources; } }
我们继续看load的过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/" ;private static final String DEFAULT_NAMES = "application" ;private void load (Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { getSearchLocations().forEach((location) -> { boolean isFolder = location.endsWith("/" ); Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; names.forEach((name) -> load(location, name, profile, filterFactory, consumer)); }); } private void load (String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { if (!StringUtils.hasText(name)) { for (PropertySourceLoader loader : this .propertySourceLoaders) { if (canLoadFileExtension(loader, location)) { load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer); return ; } } throw new IllegalStateException ("File extension of config file location '" + location + "' is not known to any PropertySourceLoader. If the location is meant to reference " + "a directory, it must end in '/'" ); } Set<String> processed = new HashSet <>(); for (PropertySourceLoader loader : this .propertySourceLoaders) { for (String fileExtension : loader.getFileExtensions()) { if (processed.add(fileExtension)) { loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer); } } } } private void loadForFileExtension (PropertySourceLoader loader, String prefix, String fileExtension, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null ); DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile); if (profile != null ) { String profileSpecificFile = prefix + "-" + profile + fileExtension; load(loader, profileSpecificFile, profile, defaultFilter, consumer); load(loader, profileSpecificFile, profile, profileFilter, consumer); for (Profile processedProfile : this .processedProfiles) { if (processedProfile != null ) { String previouslyLoaded = prefix + "-" + processedProfile + fileExtension; load(loader, previouslyLoaded, profile, profileFilter, consumer); } } } load(loader, prefix + fileExtension, profile, profileFilter, consumer); } private void load (PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) { try { Resource resource = this .resourceLoader.getResource(location); if (resource == null || !resource.exists()) { if (this .logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped missing config " , location, resource, profile); this .logger.trace(description); } return ; } if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) { if (this .logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped empty config extension " , location, resource, profile); this .logger.trace(description); } return ; } String name = "applicationConfig: [" + location + "]" ; List<Document> documents = loadDocuments(loader, name, resource); if (CollectionUtils.isEmpty(documents)) { if (this .logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped unloaded config " , location, resource, profile); this .logger.trace(description); } return ; } List<Document> loaded = new ArrayList <>(); for (Document document : documents) { if (filter.match(document)) { addActiveProfiles(document.getActiveProfiles()); addIncludedProfiles(document.getIncludeProfiles()); loaded.add(document); } } Collections.reverse(loaded); if (!loaded.isEmpty()) { loaded.forEach((document) -> consumer.accept(profile, document)); if (this .logger.isDebugEnabled()) { StringBuilder description = getDescription("Loaded config file " , location, resource, profile); this .logger.debug(description); } } } catch (Exception ex) { throw new IllegalStateException ("Failed to load property source from location '" + location + "'" , ex); } } void addActiveProfiles (Set<Profile> profiles) { if (profiles.isEmpty()) { return ; } if (this .activatedProfiles) { if (this .logger.isDebugEnabled()) { this .logger.debug("Profiles already activated, '" + profiles + "' will not be applied" ); } return ; } this .profiles.addAll(profiles); if (this .logger.isDebugEnabled()) { this .logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles)); } this .activatedProfiles = true ; removeUnprocessedDefaultProfiles(); } private void removeUnprocessedDefaultProfiles () { this .profiles.removeIf((profile) -> (profile != null && profile.isDefaultProfile())); }
当prepareEnvironment
执行完时,项目的环境变量就全部配置完了
这里一共加载了7个配置文件,取值顺序从上往下,也就是上面的会覆盖下面的同名属性。
第三步:创建容器 1 context = createApplicationContext();
跟进方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext" ; protected ConfigurableApplicationContext createApplicationContext () { Class<?> contextClass = this .applicationContextClass; if (contextClass == null ) { try { switch (this .webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break ; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break ; default : contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException ( "Unable create a default ApplicationContext, please specify an ApplicationContextClass" , ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
第四步:spring容器前置处理 1 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
这一步是刷新容器前的准备工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } if (this .lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor ()); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object [0 ])); listeners.contextLoaded(context); }
调用初始化器,在这里总算使用了前面设置的初始化器,依次进行调用。这里我们也可以自定义一些初始化器,并将其信息放入spring.factories
文件对应的key中。这是我们项目初始化的一种方式。
1 2 3 4 5 6 7 8 protected void applyInitializers (ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer." ); initializer.initialize(context); } }
加载启动指定类(重点) 之前的代码提到过,在实例化SpringApplication
实例的时候,会将HelloWorldApplication.class
存储到 this.primarySources
的属性中,现在是使用的时候了。
1 2 3 4 5 6 7 8 9 10 11 public Set<Object> getAllSources () { Set<Object> allSources = new LinkedHashSet <>(); if (!CollectionUtils.isEmpty(this .primarySources)) { allSources.addAll(this .primarySources); } if (!CollectionUtils.isEmpty(this .sources)) { allSources.addAll(this .sources); } return Collections.unmodifiableSet(allSources); }
获取了启动类之后,将它作为参数,调用load方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected void load (ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this .beanNameGenerator != null ) { loader.setBeanNameGenerator(this .beanNameGenerator); } if (this .resourceLoader != null ) { loader.setResourceLoader(this .resourceLoader); } if (this .environment != null ) { loader.setEnvironment(this .environment); } loader.load(); }
我们继续跟进,最终调用
1 2 3 4 5 6 7 8 9 10 11 12 13 private int load (Class<?> source) { if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); load(loader); } if (isComponent(source)) { this .annotatedReader.register(source); return 1 ; } return 0 ; }
启动类HelloWorldApplication.class
被加载到 beanDefinitionMap中,后续该启动类将作为开启自动化配置的入口,后面文章会继续分析,启动类是如何加载,以及自动化配置开启的详细流程。
再之后就是通知相关监听器容器已加载完。
listeners.contextLoaded(context);
第五步:刷新容器 执行到这里,springboot相关的处理工作已经结束,接下的工作就交给spring。
这一步将再之后的文章中详解
第六步:spring容器后置处理 扩展接口,设计模式中的模板方法,默认为空,如果有自定义需求,可以重写该方法,比如打印一些日志。
1 2 protected void afterRefresh (ConfigurableApplicationContext context, ApplicationArguments args) {}
第七步:通知启动结束 1 listeners.started(context);
这里和之前的通知有一些不一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void starting () { this .initialMulticaster.multicastEvent(new ApplicationStartingEvent (this .application, this .args)); } public void environmentPrepared (ConfigurableEnvironment environment) { this .initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent (this .application, this .args, environment)); } public void contextPrepared (ConfigurableApplicationContext context) { this .initialMulticaster .multicastEvent(new ApplicationContextInitializedEvent (this .application, this .args, context)); } public void started (ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent (this .application, this .args, context)); }
第八步: 执行Runners 平常,如果我们想在spring启动完成时执行一些任务都会去实现CommandLineRunner
接口中的run()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void callRunners (ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList <>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet <>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
callRunner
方法进行了重载,由此可见这两种runner只是入参不同罢了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void callRunner (ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException ("Failed to execute ApplicationRunner" , ex); } } private void callRunner (CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException ("Failed to execute CommandLineRunner" , ex); } }
至此,spring的启动流程大概就分析结束了,很有一些细节和核心部分将在之后的文章继续讲解。