通过代码实例了解SpringBoot启动原理
这篇文章主要介绍了通过代码实例了解SpringBoot启动原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
SpringBoot和Spring相比,有着不少优势,比如自动配置,jar直接运行等等。那么SpringBoot到底是怎么启动的呢?
下面是SpringBoot启动的入口:
@SpringBootApplication
publicclassHelloApplication{
publicstaticvoidmain(String[]args){
SpringApplication.run(HelloApplication.class,args);
}
}
一、先看一下@SpringBoot注解:
@Target({ElementType.TYPE})//定义其使用时机
@Retention(RetentionPolicy.RUNTIME)//编译程序将Annotation储存于class档中,可由VM使用反射机制的代码所读取和使用。
@Documented//这个注解应该被javadoc工具记录
@Inherited//被注解的类会自动继承.更具体地说,如果定义注解时使用了@Inherited标记,然后用定义的注解来标注另一个父类,父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中.
@SpringBootConfiguration//@SpringBootConfiguration就相当于@Configuration。JavaConfig配置形式
@EnableAutoConfiguration
@ComponentScan(
excludeFilters={@Filter(
type=FilterType.CUSTOM,
classes={TypeExcludeFilter.class}
),@Filter(
type=FilterType.CUSTOM,
classes={AutoConfigurationExcludeFilter.class}
)}//自动扫描并加载符合条件的组件。以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在rootpackage下,因为默认不指定basePackages。
)
public@interfaceSpringBootApplication{
@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,@ComponentScan和@EnableAutoConfiguration。
@SpringBootConfiguration和@ComponentScan,很容易知道它的意思,一个是JavaConfig配置,一个是扫描包。关键在于@EnableAutoConfiguration注解。先来看一下这个注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public@interfaceEnableAutoConfiguration{
StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";
Class>[]exclude()default{};
String[]excludeName()default{};
}
Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,如果发现注解中存在@Import(ImportSelector)的情况,就会创建一个相应的ImportSelector对象,并调用其方法publicString[]selectImports(AnnotationMetadataannotationMetadata),这里EnableAutoConfigurationImportSelector的导入@Import(EnableAutoConfigurationImportSelector.class)就属于这种情况,所以ConfigurationClassParser会实例化一个EnableAutoConfigurationImportSelector并调用它的selectImports()方法。
AutoConfigurationImportSelectorimplementsDeferredImportSelectorextendsImportSelector。
下面是AutoConfigurationImportSelector的执行过程:
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector,BeanClassLoaderAware,ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered{
privatestaticfinalString[]NO_IMPORTS=newString[0];
privatestaticfinalLoglogger=LogFactory.getLog(AutoConfigurationImportSelector.class);
privatestaticfinalStringPROPERTY_NAME_AUTOCONFIGURE_EXCLUDE="spring.autoconfigure.exclude";
privateConfigurableListableBeanFactorybeanFactory;
privateEnvironmentenvironment;
privateClassLoaderbeanClassLoader;
privateResourceLoaderresourceLoader;
publicAutoConfigurationImportSelector(){
}
publicString[]selectImports(AnnotationMetadataannotationMetadata){
if(!this.isEnabled(annotationMetadata)){
returnNO_IMPORTS;
}else{
//从配置文件中加载AutoConfigurationMetadata
AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributesattributes=this.getAttributes(annotationMetadata);
//获取所有候选配置类EnableAutoConfiguration
//使用了内部工具使用SpringFactoriesLoader,查找classpath上所有jar包中的
//META-INF\spring.factories,找出其中key为
//org.springframework.boot.autoconfigure.EnableAutoConfiguration
//的属性定义的工厂类名称。
//虽然参数有annotationMetadata,attributes,但在AutoConfigurationImportSelector的
//实现getCandidateConfigurations()中,这两个参数并未使用
Listconfigurations=this.getCandidateConfigurations(annotationMetadata,attributes);
//去重
configurations=this.removeDuplicates(configurations);
Setexclusions=this.getExclusions(annotationMetadata,attributes);
//应用exclusion属性
this.checkExcludedClasses(configurations,exclusions);
configurations.removeAll(exclusions);
//应用过滤器AutoConfigurationImportFilter,
//对于springbootautoconfigure,定义了一个需要被应用的过滤器:
//org.springframework.boot.autoconfigure.condition.OnClassCondition,
//此过滤器检查候选配置类上的注解@ConditionalOnClass,如果要求的类在classpath
//中不存在,则这个候选配置类会被排除掉
configurations=this.filter(configurations,autoConfigurationMetadata);
//现在已经找到所有需要被应用的候选配置类
//广播事件AutoConfigurationImportEvent
this.fireAutoConfigurationImportEvents(configurations,exclusions);
returnStringUtils.toStringArray(configurations);
}
}
protectedAnnotationAttributesgetAttributes(AnnotationMetadatametadata){
Stringname=this.getAnnotationClass().getName();
AnnotationAttributesattributes=AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name,true));
Assert.notNull(attributes,()->{
return"Noauto-configurationattributesfound.Is"+metadata.getClassName()+"annotatedwith"+ClassUtils.getShortName(name)+"?";
});
returnattributes;
}
protectedListgetCandidateConfigurations(AnnotationMetadatametadata,AnnotationAttributesattributes){
Listconfigurations=SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(),this.getBeanClassLoader());
Assert.notEmpty(configurations,"NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyouareusingacustompackaging,makesurethatfileiscorrect.");
returnconfigurations;
}
publicabstractclassSpringFactoriesLoader{
publicstaticfinalStringFACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";
privatestaticfinalLoglogger=LogFactory.getLog(SpringFactoriesLoader.class);
privatestaticfinalMap>cache=newConcurrentReferenceHashMap();
publicSpringFactoriesLoader(){
}
publicstaticListloadFactoryNames(Class>factoryClass,@NullableClassLoaderclassLoader){
StringfactoryClassName=factoryClass.getName();
return(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,Collections.emptyList());
}
}
privateListfilter(Listconfigurations,AutoConfigurationMetadataautoConfigurationMetadata){
longstartTime=System.nanoTime();
String[]candidates=StringUtils.toStringArray(configurations);
//记录候选配置类是否需要被排除,skip为true表示需要被排除,全部初始化为false,不需要被排除
boolean[]skip=newboolean[candidates.length];
//记录候选配置类中是否有任何一个候选配置类被忽略,初始化为false
booleanskipped=false;
Iteratorvar8=this.getAutoConfigurationImportFilters().iterator();
//获取AutoConfigurationImportFilter并逐个应用过滤
while(var8.hasNext()){
AutoConfigurationImportFilterfilter=(AutoConfigurationImportFilter)var8.next();
//对过滤器注入其需要Aware的信息
this.invokeAwareMethods(filter);
//使用此过滤器检查候选配置类跟autoConfigurationMetadata的匹配情况
boolean[]match=filter.match(candidates,autoConfigurationMetadata);
for(inti=0;iresult=newArrayList(candidates.length);
intnumberFiltered;
for(numberFiltered=0;numberFilteredgetAutoConfigurationImportFilters(){
returnSpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,this.beanClassLoader);
}
二、下面看一下SpringBoot启动时run方法执行过程
publicConfigurableApplicationContextrun(String...args){
StopWatchstopWatch=newStopWatch();
stopWatch.start();
ConfigurableApplicationContextcontext=null;
CollectionexceptionReporters=newArrayList<>();
this.configureHeadlessProperty();
//从类路径下META-INF/spring.factories获取
SpringApplicationRunListenerslisteners=getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try{
//封装命令行参数
ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironmentenvironment=prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调
SpringApplicationRunListener.environmentPrepared();表示环境准备完成
this.configureIgnoreBeanInfo(environment);
//打印Banner图
BannerprintedBanner=printBanner(environment);
//创建ApplicationContext,决定创建web的ioc还是普通的ioc
context=createApplicationContext();
//异常分析报告
exceptionReporters=getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
newClass[]{ConfigurableApplicationContext.class},context);
//准备上下文环境,将environment保存到ioc中
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//listeners.contextPrepared(context)
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded()
this.prepareContext(context,environment,listeners,applicationArguments,
printedBanner);
//刷新容器,ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat)
//扫描,创建,加载所有组件的地方,(配置类,组件,自动配置)
this.refreshContext(context);
this.afterRefresh(context,applicationArguments);
stopWatch.stop();
if(this.logStartupInfo){
newStartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(),stopWatch);
}
//所有的SpringApplicationRunListener回调started方法
listeners.started(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,
//ApplicationRunner先回调,CommandLineRunner再回调
this.callRunners(context,applicationArguments);
}
catch(Throwableex){
this.handleRunFailure(context,ex,exceptionReporters,listeners);
thrownewIllegalStateException(ex);
}
try{
//所有的SpringApplicationRunListener回调running方法
listeners.running(context);
}
catch(Throwableex){
this.handleRunFailure(context,ex,exceptionReporters,null);
thrownewIllegalStateException(ex);
}
//整个SpringBoot应用启动完成以后返回启动的ioc容器
returncontext;
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。