之前在使用spring boot框架搭建一個web服務(wù)時,一直想詳細研究下spring boot的源碼,主要是bean加載到IOC容器和Spring Aop這兩個功能的具體實現(xiàn),最近有時間就在家看了下spring關(guān)于這兩個功能的源碼光绕,也在網(wǎng)上找了些資料去看,發(fā)現(xiàn)大部分資料寫的都是偏重于某一塊源碼的講解,我是希望能夠按照spring boot的啟動流程來分析這兩個功能骤宣,這樣的話能夠前后連貫,理解起來也會更容易序愚,否則單獨講這兩部分的話憔披,很多東西不知道在哪完成初始化的,因此關(guān)于spring 源碼學習的第一篇文章就從spring boot框架的啟動開始講解。
@SpringBootApplication
public class Chapter1Application {
public static void main(String[] args) {
SpringApplication.run(Chapter1Application.class, args);
}
}
通過調(diào)用SpringApplication的run方法就可以快速啟動一個web應(yīng)用芬膝。這個run方法里最后會調(diào)用下面這個run方法望门。
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
public SpringApplication(Object... sources) {
initialize(sources);
}
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();
}
這個run方法中new了一個SpringApplication實例,在構(gòu)造函數(shù)中調(diào)用了initialize方法锰霜,在這個方法里主要做了三件事:
(1)檢查當前啟動的應(yīng)用是否是一個web應(yīng)用筹误。如果是webEnvironment 變量的值為true,檢測的方法是能否通過反射實例化下面這兩個類來判斷癣缅。
"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"
(2) 設(shè)置ApplicationContextInitializer實現(xiàn)類厨剪,這些實現(xiàn)類是配置在META-INF/spring.factories文件中,這些實現(xiàn)類后面會使用友存。
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
(3) 設(shè)置listener監(jiān)聽器祷膳,這些監(jiān)聽器也是配置在META-INF/spring.factories下如下:
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
這些監(jiān)聽器是spring啟動時,自動加載的屡立。用戶也可以自己實現(xiàn)ApplicationListener接口直晨,按照業(yè)務(wù)的要求實現(xiàn)具體的監(jiān)聽器,對spring的事件監(jiān)聽機制不熟悉的可以參考這篇文章sping監(jiān)聽器膨俐。
初始化SpringApplication實例后勇皇,調(diào)用SpringApplication的run方法,看下這個run方法實現(xiàn)吟策。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
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;
}
}
這個方法比較長儒士,spring的啟動就是在這個方法中完成的,我們還是按照步驟來分析這個方法檩坚。
(1)獲取SpringApplicationRunListeners對象着撩,獲取方式與之前獲取ApplicationListener實現(xiàn)類相同,也是從spring.factories中獲取具體的SpringApplicationRunListeners實現(xiàn)類匾委。
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener這個類的主要作用是根據(jù)spring啟動的不同時期拖叙,觸發(fā)不同的事件類型。之前設(shè)置的監(jiān)聽器赂乐,每個監(jiān)聽器對應(yīng)不同的事件薯鳍,事件類型匹配時會觸發(fā)對應(yīng)監(jiān)聽器的onApplicationEvent方法,這是一個典型的觀察者模式挨措。這個類的具體代碼可以參考EventPublishingRunListener挖滤。
(2)觸發(fā)EventPublishingRunListener的starting方法。
public void starting() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
這個方法就會觸發(fā)一個ApplicationStartedEvent事件浅役,通知這個事件對應(yīng)的監(jiān)聽器完成具體的業(yè)務(wù)斩松。
(3)創(chuàng)建ApplicationContext對象。
這個對象非常重要觉既,Spring IOC的實現(xiàn)主要就是基于這個對象惧盹∪樾遥看下怎么創(chuàng)建的。
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) {
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
根據(jù)是否是一個web應(yīng)用钧椰,確定ApplicationContext的具體類型粹断。本文以web應(yīng)用為例,初始化一個AnnotationConfigEmbeddedWebApplicationContext類嫡霞,具體初始化過程參考ApplicationContext對象初始化過程瓶埋。
(4)執(zhí)行prepareContext操作。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
诊沪。悬赏。。
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
}
這個方法中主要對上一步創(chuàng)建的ApplicationContext做一些初始化操作娄徊,調(diào)用applyInitializers方法闽颇。
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);
}
}
這個方法中會遍歷之前添加的實現(xiàn)ApplicationContextInitializer的初始化子類,然后調(diào)用這些子類的initialize方法寄锐,完成對ApplicationContext的一些初始化操作兵多。
例如:
ContextIdApplicationContextInitializer類會為ApplicationContext生成一個唯一的id。
DelegatingApplicationContextInitializer類會從application.properties配置文件中讀取key為context.initializer.classes的實現(xiàn)ApplicationContextInitializer的初始化子類橄仆,這樣就允許開發(fā)者自己添加ApplicationContextInitializer子類剩膘。
遍歷完初始化子類后,執(zhí)行l(wèi)isteners.contextPrepared(context)盆顾,這個是觸發(fā)監(jiān)聽器操作的怠褐,這個方法目前是空,不會觸發(fā)監(jiān)聽器的執(zhí)行。
然后執(zhí)行l(wèi)oad方法,加載Spring boot框架的主函數(shù)所在的類到ApplicationContext中屯阀,看下加載過程。
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();
}
初始化一個BeanDefinitionLoader類磷杏,然后調(diào)用load方法,省略中間調(diào)用過程捏卓,調(diào)用下面這個load方法极祸。
private int load(Class<?> source) {
。怠晴。遥金。
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}
return 0;
}
首先加載的類是否含有@Component注解,然后調(diào)用annotatedReader.register(source)方法蒜田,annotatedReader實例對應(yīng)的實現(xiàn)類是AnnotatedBeanDefinitionReader類稿械,忽略中間調(diào)用過程,最終會調(diào)用下面這個register方法物邑。
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
首先創(chuàng)建一個基于注解的AnnotatedGenericBeanDefinition實現(xiàn)類溜哮,這個類中保存了bean的所有信息。
然后調(diào)用this.scopeMetadataResolver.resolveScopeMetadata(abd)這個方法色解。
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}
這個方法主要是判斷類中是否有@Scope注解茂嗓,如果有這個注解會獲取這個注解對應(yīng)的value和proxyMode屬性,關(guān)于這個注解可以參考@Scope科阎。獲取完@Scope注解屬性后返回ScopeMetadata對象述吸。根據(jù)返回的ScopeMetadata對象,設(shè)置AnnotatedGenericBeanDefinition中這個bean的Scope屬性锣笨。
調(diào)用AnnotationConfigUtils.processCommonDefinitionAnnotations(abd)這個方法完成對這個類中注解的解析蝌矛,代碼如下。
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
if (metadata.isAnnotated(Lazy.class.getName())) {
abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
}
else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
}
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
if (metadata.isAnnotated(DependsOn.class.getName())) {
abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
}
if (abd instanceof AbstractBeanDefinition) {
AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
if (metadata.isAnnotated(Role.class.getName())) {
absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
}
if (metadata.isAnnotated(Description.class.getName())) {
absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
}
}
}
解析完常用注解后错英,將這個AnnotatedGenericBeanDefinition放入BeanDefinitionHolder對象中入撒,然后調(diào)用AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry)。
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
如果bean中含有@Scope注解椭岩,則根據(jù)proxyMode屬性生成一個代理對象茅逮。
生成代理對象后,將這個代理對象放入ApplicationContext中DefaultListableBeanFactory中的beanDefinitionMap這個map中判哥。
到這里prepareContext方法的大部分功能就完成了献雅。
(5)refresh Context
上一步只是初始化了context的一些參數(shù),創(chuàng)建了context里創(chuàng)建bean的DefaultListableBeanFactory塌计,將sping boot啟動的主函數(shù)類放入beanDefinitionMap這個map中挺身,項目中的其他定義的bean還未被掃描放入beanDefinitionMap,以及beanDefinitionMap中放入的bean還沒有被實例化锌仅。這些操作其實是在refresh Context這一步完成章钾。
這一步的內(nèi)容比較多,也比較復(fù)雜热芹,我們在bean加載詳細講解伍玖。
我們接下來還是繼續(xù)分析啟動的主流程。
(6)最后調(diào)用listeners.finished(context, null)
看下fininshed這個方法
public void finished(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationEvent event = getFinishedEvent(context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
if (event instanceof ApplicationFailedEvent) {
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
}
this.initialMulticaster.multicastEvent(event);
}
}
其實就是發(fā)送一個ApplicationReadyEvent事件觸發(fā)監(jiān)聽此事件的監(jiān)聽器剿吻,我看了下之前加載的監(jiān)聽器好像沒有監(jiān)聽此事件的監(jiān)聽器窍箍,當然用戶也可以自己定義關(guān)于此事件的監(jiān)聽器。
到這里springboot 啟動的大概流程就分析完了丽旅,大部分的文章對這部分都是一概而過椰棘,直接去講bean的加載,但是這里還是有很多內(nèi)容需要清楚的榄笙,這樣才能更明白類的調(diào)用關(guān)系邪狞、監(jiān)聽器模式、bean是如何加載的茅撞。
下一節(jié)就具體講bean加載帆卓。