写在前面
随着微服务时代的到来,spring boot基本上是所有java开发人员标配的技术栈了,我们几乎每天都在和spring打着交到,所以深入理解spring的理念和细节就十分重要。但是,不得不说spring是一个庞大的体系,庞大到几乎所有的组件,spring都会有一套对应的解决方案。虽然写一个系统的深入spring的文章已经计划了很久,但是由于它的庞大和复杂,让我一直找不到头绪。所以我尝试着一点一点的将spring的核心链路整理成一个系列文章,一是让自己把spring系统的学习一遍,二也是想寻找志同道合的人一起学习和讨论,相互提高嘛。
Spring Boot启动
入口
对于spring boot的启动方式分为两种,第一种是通过fatjar的方式进行启动,另一个是通过IED启动。本文主要关注Springboot启动流程,对于fatjar的启动方式后面单独介绍。
spring boot的启动十分简单,当然这就是它的初衷,配置简单化:
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
静态方法run,创建了一个SpringApplication并运行它,这里用静态方法创建实例并运行还是比较清晰(至少比调用构造函数的好):
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param sources the sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
spring boot的准备工作
构造函数对SpringApplication做了一个初始化,主要是构建Spring的基本运行环境:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
deduceWebEnvironment主要是判断当前环境是否为web环境,因为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 主要是获取当前环境下配置的ApplicationContextInitializer,方法是通过classloader读取当前环境下所有的META-INF/spring.factories文件中的配置,来进行实例化。这里classLoader可以获取多个spring.factories配置,我们也可以使用这个特性去做一些扩展:
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);
}
}
像Spring boot本身定义的spring.factories:
# 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.web.ServerPortInfoApplicationContextInitializer
然后通过反射进行实例化:
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(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;
}
然后保存造SpringApplication的成员变量中:
/**
* Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
* {@link ApplicationContext}.
* @param initializers the initializers to set
*/
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}
同样的,加载ApplicationContextInitializer之后对ApplicationListener进行了加载,下面是SpringBoot本身的Listener:
# 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
/**
* Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
* and registered with the {@link ApplicationContext}.
* @param listeners the listeners to set
*/
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<ApplicationListener<?>>();
this.listeners.addAll(listeners);
}
启动spring boot
一切就绪后,调用run方法,启动Spring Boot:
/**
* 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;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
SpringBoot启动事件发送
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
这里面代码其实比较熟悉了,就是获取SpringApplicationRunListener,构建SpringApplicationRunListeners,并发送started事件,那么整个事件发送给谁?
其实SpringApplicationRunListener 在spring boot中配置的只有EventPublishingRunListener:
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);
}
}
@Override
public void started() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
其实也就是将事件发送给前面我们初始化的Listeners:
# 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
当然不是通知所有的Listeners,Springboot这里会根据event和listener类型做一个过滤:
/**
* Return a Collection of ApplicationListeners matching the given
* event type. Non-matching listeners get excluded early.
* @param event the event to be propagated. Allows for excluding
* non-matching listeners early, based on cached matching information.
* @param eventType the event type
* @return a Collection of ApplicationListeners
* @see org.springframework.context.ApplicationListener
*/
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
这里一个会被调用的LoggingApplicationListener就是一个比较重要的Listener。
environment构建
spring 在启动之前会读取环境变量、jvm启动参数和自身的配置文件,加载这些配置文件到environment:
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
return environment;
}
首先加载环境变量和JVM启动参数:
/**
* Create a new {@code Environment} instance, calling back to
* {@link #customizePropertySources(MutablePropertySources)} during construction to
* allow subclasses to contribute or manipulate {@link PropertySource} instances as
* appropriate.
* @see #customizePropertySources(MutablePropertySources)
*/
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
if (this.logger.isDebugEnabled()) {
this.logger.debug(String.format(
"Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));
}
}
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
如果为WEB环境,那么会加载Servlet相关的配置:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
接下来加载spring的配置:
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
通过configureProfiles加载Profile:
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
}
environment.getActiveProfiles在Environment中获取Profile的配置(读取环境变量中的spring.profiles.active配置):
protected Set<String> doGetActiveProfiles() {
synchronized (this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
然后把additionalProfiles和activeProfiles做一个合并,set到ActiveProfiles中。
上面主要是对当前环境指定的Profile做一个汇总,加载是通过发送ApplicationEnvironmentPreparedEvent给对应的ConfigFileApplicationListener进行Profile解析:
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());
}
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
configureIgnoreBeanInfo(environment);
bindToSpringApplication(environment, application);
}
addPropertySources中对Profile进行加载,获取之前加载的Profile和default Profile:
public void load() {
this.propertiesLoader = new PropertySourcesLoader();
this.activatedProfiles = false;
this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
this.processedProfiles = new LinkedList<Profile>();
// Pre-existing active profiles set via Environment.setActiveProfiles()
// are additional profiles and config files are allowed to add more if
// they want to, so don't call addActiveProfiles() here.
Set<Profile> initialActiveProfiles = initializeActiveProfiles();
this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
if (this.profiles.isEmpty()) {
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
if (!this.profiles.contains(defaultProfile)) {
this.profiles.add(defaultProfile);
}
}
}
// The default profile for these purposes is represented as null. We add it
// last so that it is first out of the queue (active profiles will then
// override any settings in the defaults when the list is reversed later).
this.profiles.add(null);
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
for (String location : getSearchLocations()) {
if (!location.endsWith("/")) {
// location is a filename already, so don't search for more
// filenames
load(location, null, profile);
}
else {
for (String name : getSearchNames()) {
load(location, name, profile);
}
}
}
this.processedProfiles.add(profile);
}
addConfigurationProperties(this.propertiesLoader.getPropertySources());
}
getSearchLocations获取Profile的搜索路径,这里可以通过spring.config.location配置,来新增搜索路径,除了设置路径还有几个默认的搜索路径(classpath:/,classpath:/config/,file:./,file:./config/)
private Set<String> getSearchLocations() {
Set<String> locations = new LinkedHashSet<String>();
// User-configured settings take precedence, so we do them first
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
for (String path : asResolvedSet(
this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
if (!path.contains("$")) {
path = StringUtils.cleanPath(path);
if (!ResourceUtils.isUrl(path)) {
path = ResourceUtils.FILE_URL_PREFIX + path;
}
}
locations.add(path);
}
}
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
DEFAULT_SEARCH_LOCATIONS));
return locations;
}
getSearchNames获取搜索的文件名称,这里可以通过spring.config.name设置配置名称,这里也有一个默认的配置名称application,默认搜索application.properties文件:
private Set<String> getSearchNames() {
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
null);
}
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
然后做Profile的加载,上文可以看到一句this.profiles.add(null),这一句主要为了加载默认的Profile配置。还有一个Profile 名为default,主要是为了加载application-default.properties。这里的文件命名规则都是进行默认拼接的:location + name + "-" + profile + "." + ext
private void load(String location, String name, Profile profile) {
String group = "profile=" + (profile == null ? "" : profile);
if (!StringUtils.hasText(name)) {
// Try to load directly from the location
loadIntoGroup(group, location, profile);
}
else {
// Search for a file with the given name
for (String ext : this.propertiesLoader.getAllFileExtensions()) {
if (profile != null) {
// Try the profile-specific file
loadIntoGroup(group, location + name + "-" + profile + "." + ext,
null);
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
loadIntoGroup(group, location + name + "-"
+ processedProfile + "." + ext, profile);
}
}
// Sometimes people put "spring.profiles: dev" in
// application-dev.yml (gh-340). Arguably we should try and error
// out on that, but we can be kind and load it anyway.
loadIntoGroup(group, location + name + "-" + profile + "." + ext,
profile);
}
// Also try the profile-specific section (if any) of the normal file
loadIntoGroup(group, location + name + "." + ext, profile);
}
}
}
这里值得一提的是,对于application.properties中也可以对配置文件进行配置,然后新增一个Profile:
private void handleProfileProperties(PropertySource<?> propertySource) {
Set<Profile> activeProfiles = getProfilesForValue(
propertySource.getProperty(ACTIVE_PROFILES_PROPERTY));
maybeActivateProfiles(activeProfiles);
Set<Profile> includeProfiles = getProfilesForValue(
propertySource.getProperty(INCLUDE_PROFILES_PROPERTY));
addProfiles(includeProfiles);
}
通过在application.properties中定义spring.profiles.active,可以加载指定的application-${spring.profiles.active}的配置文件
/**
* The "active profiles" property name.
*/
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
/**
* The "includes profiles" property name.
*/
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
创建ApplicationContext
createApplicationContext通过判断是否为Web环境来创建ApplicationContext,非web环境为:AnnotationConfigApplicationContext,web环境为:AnnotationConfigEmbeddedWebApplicationContext。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
这里初始换ApplicationContext是很重要的,因为在实例化ApplicationContext的时候注册了很多的Processor,这些Processer被用来处理各种@Annotation,这里主要涉及Spring的实现,后面会专门针对Spring的Bean加载再写一些篇专题文章,这里就不展开了。
上下文准备:
prepareContext主要是做ApplicationContext refresh前的一些准备工作,例如调用所有的Initializers,来做些初始化的工作,同时加载启动类,并发送一些事件给到对应监听的listener
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);//将Environment传递到上下文中
postProcessApplicationContext(context);
applyInitializers(context);//调用前文中声明的Initializers,Initializer大多是注册Listener或Processor到ApplictionContext。
listeners.contextPrepared(context);//发送contextPreparedEvent
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();//获取SpringApplication的source成员,一般为springboot的启动类。
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));//加载启动类到ApplicationContext,后续利用source类加载其他声明的bean
listeners.contextLoaded(context);//发送ContextLoaded
}
这里值得一提的是Load Resource,Load Resource主要是用来加载Spring boot的启动类,启动类上一般会标识@Import、scanPackage等等一些配置,这些是整个Spring容器加载Bean需要的入口类。
refreshContext
refreshContext不多说,主要是refresh ApplicationContext ,里面逻辑十分复杂,很难一下子说全说透,本文主要是针对springBoot的启动流程,关于Spring的启动和加载,后续再写一些专题讨论:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
finished
这一步主要是做Spring初始化之后的回调和通知工作,afterRefresh通过调用BeanFactory中定义的ApplicationRunner和CommandLineRunner来做context初始化之后的逻辑,listeners.finished主要是通知监听的Listeners。
至此,Spring boot已经启动完成。
总结
springBoot启动流程图.jpgSpring Boot的启动流程是比较长的,但是也还是给我们留下了很多的类似于SPI的扩展点去实现我们的功能,尤其是Initializer,除了允许我们可以在ApplicationContext没有Refresh的时候做一些初始化的事情,同时还提供了一个无侵入的方式将我们自己插件(starter)中需要用到的processor和Listener等扩展注入到ApplicationContext中。
在整个启动过程中Spring boot会有一些Event发出,这也就给了我们在不同阶段实现自己的扩展逻辑的机会,实现起来也比较简单,监听Event即可。
除了Spring本身的功能配置的初始化之外,Spring Boot还要做Spring容器的初始化,因为Spring容器的初始化过程过于复杂,不能在这张图中展示出来。后面会拿出专题来写一下Spring的内部的运转机制。
后续计划
Spring Boot的启动流程算是一个开头,对于诸多的扩展Initializer、Listener以及Spring的内容都没有详细的写,毕竟Sping太大,内容太多,不是三言两语能说明白的。后面会把整个流程的各个节点单独的拆开来详细的学习一下,循序渐进的把Spring系统的学习一下。
当然也不只是分析源码,之所以然更要知其所以然,对于好的思想和实现,理解的同时加以实践才是最好的。
网友评论