入口
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
以上是一个最简单的Springboot程序(2.0.3版本)示例,也是我们最通用的写法,但其中其实封装这一系列复杂的功能操作,让我们开始逐步进行分析。
首先这里最重要的必然是注解@SpringBootApplication
@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
@SpringBootApplication
注解由几个注解复合组成,其中最主要的就是@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
这三个。
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
其中的@ComponentScan
是spring的原生注解,@SpringBootConfiguration
虽然是springboot中的注解,但其实质就是包装后的@Configuration
,仍然是spring中的注解,用于代替xml的方式管理配置bean
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@EnableAutoConfiguration
的定义如上,这里最重要的注解是@Import
(@AutoConfigurationPackage
注解的实现也是基于@Import
),借助@Import
的帮助,将所有符合自动配置条件的bean定义加载到IoC容器中。关于@EnableAutoConfiguration
注解后续涉及到时会再详细说明。这里我们先回到启动类的run
方法从头分析初始化流程。
run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
可以看到'run'方法最终调用的是new SpringApplication(primarySources).run(args)
,这里首先创建了SpringApplication
对象,然后调用其run
方法
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
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 = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这里主要是为SpringApplication
对象进行初始化,这里要专门提一下的是webApplicationType
和getSpringFactoriesInstances
。
webApplicationType
它用来标识我们的应用是什么类型的应用,来看一下deduceWebApplicationType()
方法的实现
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
其返回值是WebApplicationType
类型的枚举类,其值有NONE
、SERVLET
、REACTIVE
三种,分别对应非WEB应用,基于servlet的WEB应用和基于reactive的WEB应用。
getSpringFactoriesInstances
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 = Thread.currentThread().getContextClassLoader();
// 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.loadFactoryNames(type, classLoader)
方法,来看一下
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
重点关注一下loadSpringFactories(classLoader)
做了什么
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()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这里的FACTORIES_RESOURCE_LOCATION
定义为META-INF/spring.factories
,因此该方法会扫描所有包下的该文件,将其解析成map对象并缓存到cache
中以避免重复加载,springboot包下该文件的部分片段如下
# 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.web.context.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.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
从这里可以看出,setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class))
和setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
分别对应设置的是上述这些类。
解析完成后调用createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)
处理解析结果,生成对应的实例,源码如下
@SuppressWarnings("unchecked")
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;
}
这里的核心是通过ClassUtils.forName(name, classLoader)
方法,以反射的方式生成类实例instanceClass
。由此可以看出SpringFactoriesLoader.loadFactoryNames(type, classLoader)
的作用就是将META-INF/spring.factories
中配置的内容进行实例化的工厂方法类,具备很强的扩展性,与SPI机制有异曲同工
的效果。
看完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);//获取并启动监听器
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;
}
这里挑其中比较重要的几个方法进行分析
- 创建
ConfigurableEnvironment
对象
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();//初始化environment
configureEnvironment(environment, applicationArguments.getSourceArgs());//加载默认配置
listeners.environmentPrepared(environment);//通知环境监听器,加载项目中的配置文件
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
通过getOrCreateEnvironment()
方法创建容器环境
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
可以看到environment
存在则不会重复创建,当应用类型为servlet时创建的是StandardServletEnvironment
对象,否则创建StandardEnvironment
对象。
接着来看configureEnvironment(environment, applicationArguments.getSourceArgs())
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);//加载启动命令行配置属性
configureProfiles(environment, args);//设置active属性
}
configurePropertySources(environment, args)
加载启动命令行的配置属性,来看一下实现
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
//加载命令行配置
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
这里的MutablePropertySources
对象用于存储配置集合,其内部维护了一个CopyOnWriteArrayList
类型的list对象,当默认配置存在时,会向该list的尾部插入一个new MapPropertySource("defaultProperties", this.defaultProperties)
对象。
接着来看configureProfiles(environment, args)
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<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
这里主要做的事情就是获取environment.getActiveProfiles()
的参数设置到environment
中,即spring.profiles.active
对应的环境变量。
最后来看一下listeners.environmentPrepared(environment)
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
这里的listeners
就是之前通过META-INF/spring.factories
注册的所有listeners,后面我们先以其中最重要的ConfigFileApplicationListener
做为例子进行分析,接着来看listener.environmentPrepared(environment)
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
可以看到这里创建了一个ApplicationEnvironmentPreparedEvent
类型的事件,并且调用了multicastEvent
方法,通过该方法最终会调用到listener的onApplicationEvent
方法,触发事件监听器的执行。
接下来具体看一下ConfigFileApplicationListener
的onApplicationEvent
方法做了什么
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
可以看到当监听到ApplicationEnvironmentPreparedEvent
类型的事件时,调用onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event)
方法
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());
}
}
可以看到这里通过loadPostProcessors()
方法加载了META-INF/spring.factories
中的所有EnvironmentPostProcessor
类到list中,同时把ConfigFileApplicationListener
自己也添加进去了。接着遍历list中所有对象,并执行postProcessEnvironment
方法,于是接着来看该方法
@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();
}
这里的核心是new Loader(environment, resourceLoader).load()
,这里的Loader
是一个内部类,用于处理配置文件的加载,首先看一下其构造方法
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = (resourceLoader != null ? resourceLoader
: new DefaultResourceLoader());
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}
可以看到这里的resourceLoader
又是通过SpringFactoriesLoader
进行加载,那么来看看META-INF/spring.factories
中定义了哪些resourceLoader
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
从名字就可以看出来,PropertiesPropertySourceLoader
和YamlPropertySourceLoader
分别用于处理.properties和.yml类型的配置文件。
接着来看看load()
方法做了什么
public void load() {
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 (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));//对加载过的配置文件进行排序
addLoadedPropertySources();
}
initializeProfiles()
进行了profiles
的初始化,默认会添加null
和default
到profiles
中,null
对应配置文件application.properties和application.yml,default
对应配置文件application-default.yml和application-default.properties,这里的null
会被优先处理,由于后处理的会覆盖先处理的,因此其优先级最低。
接着来看load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false))
方法
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));
});
}
这里重点是通过getSearchLocations()
获取配置文件的路径,默认会获得4个路径
- file:./config/
- file:./
- classpath:/config/
- classpath:/
接着会遍历这些路径,拼接配置文件名称,选择合适的yml或者properties解析器进行解析,最后将结果添加到environment
的propertySources
中。
- 通过
createApplicationContext()
创建run
方法的返回值对象context
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
可以看到这里也是根据webApplicationType
的取值,分别创建不同的返回类型。
- 通过
prepareContext(context, environment, listeners, applicationArguments,printedBanner)
方法将listeners
、environment
、applicationArguments
、printedBanner
等重要组件与上下文对象context
进行关联
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);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();//获取启动类
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));//加载我们的启动类,将启动类注入容器
listeners.contextLoaded(context);//发布容器已加载事件。
}
这里的sources
装的就是我们的启动类,然后通过load(context, sources.toArray(new Object[0]))
方法进行加载
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();
}
来看一下loader
是如何被加载的
public int load() {
int count = 0;
for (Object source : this.sources) {
count += load(source);
}
return count;
}
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
private int load(Class<?> source) {
if (isGroovyPresent()
&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
GroovyBeanDefinitionSource.class);
load(loader);
}
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}
return 0;
}
经过一系列调用之后最终由load(Class<?> source)
方法执行,这里比较有趣的是当Groovy存在时居然是优先调用Groovy的方式进行加载,否则才走this.annotatedReader.register(source)
方法将启动类注册到beanDefinitionMap
中。
-
refreshContext(context)
刷新容器
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
这个refresh()
方法相当重要,尤其是invokeBeanFactoryPostProcessors(beanFactory)
,这是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键部分,后续再详细讲解。
总结
至此Springboot的启动流程已经大体分析完了,也了解了配置文件和启动类分别是是如何被加载的,但仍有两个问题待解,一是Springboot的核心思想约定大于配置是如何做到的,二是Springboot的各种spring-boot-starter-*是如何发挥作用的,这两个问题留待后续文章继续分析。