springboot的启动类入口
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication注解
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
其中比较重要的有三个注解,分别是:
1)@SpringBootConfiguration // 继承了Configuration,表示当前是注解类,会被扫描并加载到IOC容器
2)@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
3)@ComponentScan(excludeFilters = { // 扫描路径设置}),
//自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在root
package下,因为默认不指定basePackages
@EnableAutoConfiguration
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage【重点注解】
@Import(AutoConfigurationImportSelector.class)【重点注解】
public @interface EnableAutoConfiguration {
...
}
其中有两个重点注解,下面分别介绍:
@AutoConfigurationPackage注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
通过@Import(AutoConfigurationPackages.Registrar.class)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
……
}
注册当前启动类的根package;
注册org.springframework.boot.autoconfigure.AutoConfigurationPackages的BeanDefinition。
@Import(AutoConfigurationImportSelector.class)注解
可以从图中看出 AutoConfigurationImportSelector 实现了 DeferredImportSelector 从 ImportSelector继承的方法:selectImports。
该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;
如果获取到类信息,spring可以通过类加载器将类加载到jvm中。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
其中this.getCandidateConfigurations(annotationMetadata, attributes)方法调用了SpringFactoriesLoader#loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader),实际上是找到所有jar包以及项目中的META-INF/spring.factories文件,加载依赖包及项目中的bean进入IOC容器中。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
@EnableAutoConfiguration自动装配其实就变成了:
从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
启动源码
入口:
SpringApplication.run(AppTenderApplicationExt.class);
总流程
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
//创建引导上下文(Context环境)
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
//设置headless无头模式
this.configureHeadlessProperty();
//获取所有运行监听器(为了方便所有Listener进行事件感知)
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//遍历所有监听器,并且starting
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//保存命令行中所有的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境信息
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//配置需要忽略的环境信息
this.configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = this.printBanner(environment);
//创建IOC容器
context = this.createApplicationContext();
//记录当前事件
context.setApplicationStartup(this.applicationStartup);
//准备IOC容器信息(准备ApplicationContext)
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新IOC容器
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
//监听容器启动花费了多长时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
//listener通知项目启动
listeners.started(context, timeTakenToStartup);
//调用所有的runners
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
return context;
} catch (Throwable var11) {
this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var11);
}
}
分步
1、 createBootstrapContext
private DefaultBootstrapContext createBootstrapContext() {
//首先创建DefaultBootstrapContext对象,保存信息。
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
//接着获取所有的initializer并执行initialize(bootstrapContext)),来完成对引导启动器的环境设置让当前应用进入headless模式
this.bootstrapRegistryInitializers.forEach((initializer) -> {
initializer.initialize(bootstrapContext);
});
return bootstrapContext;
}
2、configureHeadlessProperty
headless模式的意思就是明确Springboot要在无鼠键支持的环境中运行,一般程序也都跑在Linux之类的服务器上,无鼠键支持,这里默认值是true
private void configureHeadlessProperty() {
System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
}
3、getRunListeners
获取所有运行监听器(为了方便所有Listener进行事件感知)
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
//从spring.factories中寻找SpringApplicationRunListener.class
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
4、listeners.starting
遍历所有监听器,并且starting。
doWithListeners中的foreach方法,相当于通知所有感兴趣(需要这个信息)系统正在启动的“人”,项目正在starting
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
this.doWithListeners("spring.boot.application.starting", (listener) -> {
listener.starting(bootstrapContext);
}, (step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
5、prepareEnvironment
准备环境信息
在配置环境信息时:
ConfigurationPropertySources.attach(environment);
中有MutablePropertySources sources=environment.getPropertySources();能够获取@PropertySource,读取到外部的配置文件,也就是说此方法加载全系统中所有的配置信息!
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//返回或创建基础环境信息对象
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//通过命令行参数和环境信息配置环境
this.configureEnvironment(environment, applicationArguments.getSourceArgs());
//绑定信息,也就是保存工作
ConfigurationPropertySources.attach(environment);
//listeners就是之前获取到的所有的RunListener,通知所有监听器当前环境准备完成
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
//绑定环境信息
this.bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
//能够获取@PropertySource,读取到外部的配置文件,也就是说此方法加载全系统中所有的配置信息!
MutablePropertySources sources = ((ConfigurableEnvironment)environment).getPropertySources();
PropertySource<?> attached = getAttached(sources);
if (attached == null || !isUsingSources((PropertySource)attached, sources)) {
attached = new ConfigurationPropertySourcesPropertySource("configurationProperties", new SpringConfigurationPropertySources(sources));
}
sources.remove("configurationProperties");
sources.addFirst((PropertySource)attached);
}
6、createApplicationContext
创建IOC容器
根据当前项目类型(Servlet、reactive、default)创建
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
7、prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//保存基本环境信息
context.setEnvironment(environment);
//IOC容器的后置处理流程,像注册一些组件,读取配置文件的资源,资源的加载器
this.postProcessApplicationContext(context);
//应用初始化器,遍历所有的ApplicationContextInitializer,调用initialize(context);来对IOC容器进行初始化(扩展工作)
也就说之前保存的ApplicationContextInitializer在此时调用了。
this.applyInitializers(context);
//通知所有的监听器IOC容器的上下文配置完毕
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
//拿到Bean工厂,注册单实例
(项目中的参数会作为组件注册进去)
banner也是IOC容器中的一个组件
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
//所有监听器调用contextLoaded,通知IOC容器已经加载完毕
listeners.contextLoaded(context);
}
8、refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
this.refresh(context);
}
9、callRunners
获取容器中的Runner分别是
ApplicationRunner
CommandLineRunner
合并所有Runner并且按照@Order(优先级)进行排序
遍历所有的Runner调用run方法
异常处理
调用Listener的failed方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}