入口
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
以上是一個最簡單的Springboot程序(2.0.3版本)示例,也是我們最通用的寫法,但其中其實封裝這一系列復(fù)雜的功能操作,讓我們開始逐步進(jìn)行分析。
首先這里最重要的必然是注解@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注解由幾個注解復(fù)合組成,其中最主要的就是@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan這三個。
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
其中的@ComponentScan是spring的原生注解,@SpringBootConfiguration雖然是springboot中的注解,但其實質(zhì)就是包裝后的@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注解的實現(xiàn)也是基于@Import),借助@Import的幫助,將所有符合自動配置條件的bean定義加載到IoC容器中。關(guān)于@EnableAutoConfiguration注解后續(xù)涉及到時會再詳細(xì)說明。這里我們先回到啟動類的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'方法最終調(diào)用的是new SpringApplication(primarySources).run(args),這里首先創(chuàng)建了SpringApplication對象,然后調(diào)用其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對象進(jìn)行初始化,這里要專門提一下的是webApplicationType和getSpringFactoriesInstances。
webApplicationType
它用來標(biāo)識我們的應(yīng)用是什么類型的應(yīng)用,來看一下deduceWebApplicationType()方法的實現(xiàn)
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三種,分別對應(yīng)非WEB應(yīng)用,基于servlet的WEB應(yīng)用和基于reactive的WEB應(yīng)用。
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());
}
重點關(guān)注一下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中以避免重復(fù)加載,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));分別對應(yīng)設(shè)置的是上述這些類。
解析完成后調(diào)用createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)處理解析結(jié)果,生成對應(yīng)的實例,源碼如下
@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中配置的內(nèi)容進(jìn)行實例化的工廠方法類,具備很強(qiáng)的擴(kuò)展性,與SPI機(jī)制有異曲同工
的效果。
看完SpringApplication的初始化,接著跳回run方法繼續(xù)分析
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);//獲取并啟動監(jiān)聽器
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);//構(gòu)造容器環(huán)境
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();//創(chuàng)建容器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);//準(zhǔn)備容器
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;
}
這里挑其中比較重要的幾個方法進(jìn)行分析
- 創(chuàng)建
ConfigurableEnvironment對象
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();//初始化environment
configureEnvironment(environment, applicationArguments.getSourceArgs());//加載默認(rèn)配置
listeners.environmentPrepared(environment);//通知環(huán)境監(jiān)聽器,加載項目中的配置文件
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
通過getOrCreateEnvironment()方法創(chuàng)建容器環(huán)境
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
可以看到environment存在則不會重復(fù)創(chuàng)建,當(dāng)應(yīng)用類型為servlet時創(chuàng)建的是StandardServletEnvironment對象,否則創(chuàng)建StandardEnvironment對象。
接著來看configureEnvironment(environment, applicationArguments.getSourceArgs())
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);//加載啟動命令行配置屬性
configureProfiles(environment, args);//設(shè)置active屬性
}
configurePropertySources(environment, args)加載啟動命令行的配置屬性,來看一下實現(xiàn)
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對象用于存儲配置集合,其內(nèi)部維護(hù)了一個CopyOnWriteArrayList類型的list對象,當(dāng)默認(rèn)配置存在時,會向該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()的參數(shù)設(shè)置到environment中,即spring.profiles.active對應(yīng)的環(huán)境變量。
最后來看一下listeners.environmentPrepared(environment)
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
這里的listeners就是之前通過META-INF/spring.factories注冊的所有l(wèi)isteners,后面我們先以其中最重要的ConfigFileApplicationListener做為例子進(jìn)行分析,接著來看listener.environmentPrepared(environment)
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
可以看到這里創(chuàng)建了一個ApplicationEnvironmentPreparedEvent類型的事件,并且調(diào)用了multicastEvent方法,通過該方法最終會調(diào)用到listener的onApplicationEvent方法,觸發(fā)事件監(jiān)聽器的執(zhí)行。
接下來具體看一下ConfigFileApplicationListener的onApplicationEvent方法做了什么
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
可以看到當(dāng)監(jiān)聽到ApplicationEnvironmentPreparedEvent類型的事件時,調(diào)用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自己也添加進(jìn)去了。接著遍歷list中所有對象,并執(zhí)行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是一個內(nèi)部類,用于處理配置文件的加載,首先看一下其構(gòu)造方法
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進(jìn)行加載,那么來看看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));//對加載過的配置文件進(jìn)行排序
addLoadedPropertySources();
}
initializeProfiles()進(jìn)行了profiles的初始化,默認(rèn)會添加null和default到profiles中,null對應(yīng)配置文件application.properties和application.yml,default對應(yīng)配置文件application-default.yml和application-default.properties,這里的null會被優(yōu)先處理,由于后處理的會覆蓋先處理的,因此其優(yōu)先級最低。
接著來看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()獲取配置文件的路徑,默認(rèn)會獲得4個路徑
- file:./config/
- file:./
- classpath:/config/
- classpath:/
接著會遍歷這些路徑,拼接配置文件名稱,選擇合適的yml或者properties解析器進(jìn)行解析,最后將結(jié)果添加到environment的propertySources中。
- 通過
createApplicationContext()創(chuàng)建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);
}
可以看到這里也是根據(jù)webApplicationType的取值,分別創(chuàng)建不同的返回類型。
- 通過
prepareContext(context, environment, listeners, applicationArguments,printedBanner)方法將listeners、environment、applicationArguments、printedBanner等重要組件與上下文對象context進(jìn)行關(guān)聯(lián)
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);//設(shè)置容器環(huán)境,包括各種變量
postProcessApplicationContext(context);//執(zhí)行容器后置處理,為自定義預(yù)留擴(kuò)展
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);//發(fā)布容器已加載事件。
}
這里的sources裝的就是我們的啟動類,然后通過load(context, sources.toArray(new Object[0]))方法進(jìn)行加載
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;
}
經(jīng)過一系列調(diào)用之后最終由load(Class<?> source)方法執(zhí)行,這里比較有趣的是當(dāng)Groovy存在時居然是優(yōu)先調(diào)用Groovy的方式進(jìn)行加載,否則才走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()方法相當(dāng)重要,尤其是invokeBeanFactoryPostProcessors(beanFactory),這是實現(xiàn)spring-boot-starter-*(mybatis、redis等)自動化配置的關(guān)鍵部分,后續(xù)再詳細(xì)講解。
總結(jié)
至此Springboot的啟動流程已經(jīng)大體分析完了,也了解了配置文件和啟動類分別是是如何被加載的,但仍有兩個問題待解,一是Springboot的核心思想約定大于配置是如何做到的,二是Springboot的各種spring-boot-starter-*是如何發(fā)揮作用的,這兩個問題留待后續(xù)文章繼續(xù)分析。