Spring Boot项目,都会用到如下的启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication是springboot的基本注解,是写在springboot的启动类上的注解,目的是开启springboot的自动配置。
1 @SpringBootApplication
@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:
@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
@SpringBootApplication看作是下列注解的集合
- @SpringBootConfiguration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
- @ComponentScan:扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类
- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
1.1 @SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {...}
任何一个标注了@Configuration
的Java类定义都是一个JavaConfig配置类
@Configuration
的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
@Configuration
public class MockConfiguration{
//bean定义
}
1.2 @ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
...
}
@ComponentScan
的功能其实就是自动扫描并加载符合条件的组件(比如@Component
和@Repository
等)或者bean定义,最终将这些bean定义加载到IoC容器中。
basePackages等属性来细粒度的定制@ComponentScan
自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan
所在类的package进行扫描(SpringBoot的启动类最好是放在root package下)。
1.3 @EnableAutoConfiguration
@EnableAutoConfiguration定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}
定义中有两个注解
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
1.3.1 @AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {...}
@AutoConfigurationPackage
中使用注解@Import
导入了AutoConfigurationPackages.Registrar.class
到容器中。
@AutoConfigurationPackage
就是添加该注解的类所在的包作为自动配置包进行管理。实现上就是依赖于工具类AutoConfigurationPackages中的内部类Registrar对所标注的包进行注册。
1.3.2 AutoConfigurationImportSelector.class
作用
AutoConfigurationImportSelector
的作用是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration
对应的配置类通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总加载到IoC容器。
注意 这些配置类一般使用了@ConditionalOnClass,需满足一定的条件才会激活配置。
源码分析
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
// 所返回的字符串数组为 所有的将要被导入的类的全类名
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
... }
getAutoConfigurationEntry
方法调用了getCandidateConfigurations
方法,
在调用SpringFactoriesLoader.loadFactoryNames
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
SpringFactoriesLoader.loadFactoryNames
方法调用loadSpringFactories方
法从所有的jar包中读取META-INF/spring.factories
文件信息。
//这个方法返回的为Map<String, List<String>> 变量result。
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//从全局变量cache中以classLoader为key获取所对应的value
Map<String, List<String>> result = cache.get(classLoader);
//判断在cache中是否存在相对应的value,如果已经存在则直接返回对应的result
if (result != null) {
return result;
}
//至此,已经判断得出result为空,所以实例化一个新的HashMap
result = new HashMap<>();
try {
//从传入的类加载器中获取资源,路径为"META-INF/spring.factories"
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
//将获取到的urls进行遍历
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//获取资源的url在当前项目中的位置等
UrlResource resource = new UrlResource(url);
//通过配置类加载工具类加载配置类,获取所有配置
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//遍历所有配置实体
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
//遍历当前配置实体中的所有value,如果不存在当前key,则将当前key以及下面遍历所得到的value一起添加到result中,如果存在则将该value添加到所对应的key下面。
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements(译文:用包含唯一元素的不可修改列表替换所有列表)
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
//将该classLoader以及对应的result添加到cache中
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
//至此,返回了META-INF/spring.factories中的所有的配置
return result;
}
应用
下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容
1.4 流程图
2 启动流程
2.1 创建SpringApplication实例
启动类调的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);
}
SpringApplication 构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//启动类传入的Class
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前项目类型,有三种:NONE、SERVLET、REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//判断主类,初始化入口类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2 run方法调用
2.2.1 run方法主要流程
public ConfigurableApplicationContext run(String... args) {
//创建计时器,开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//定义上下文对象
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//Headless模式设置
configureHeadlessProperty();
//加载SpringApplicationRunListeners监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//发送ApplicationStartingEvent事件
listeners.starting();
try {
//封装ApplicationArguments对象
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=8000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//配置环境
// 创建并配置当前SpringBoot应用将要使用的Environment
// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//根据环境信息配置要忽略的bean信息
configureIgnoreBeanInfo(environment);
//打印Banner标志
Banner printedBanner = printBanner(environment);
//创建Spring容器
context = createApplicationContext();
//加载SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//容器初始化:包含一个非常关键的操作,将启动类注入容器,为后续开启自动化配置奠定基础。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文:此方法用于解析配置文件,加载 bean 对象,并且启动内置的 web 容器等操作
refreshContext(context);
//刷新后的操作,由子类去扩展
afterRefresh(context, applicationArguments);
//计时结束
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发送ApplicationStartedEvent事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
listeners.started(context);
//查找容器中注册有CommandLineRunner或者ApplicationRunner的bean,遍历并执行run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//发送ApplicationReadyEvent事件,触发所有的 SpringApplicationRunListener 监听器的 running 事件
listeners.running(context);
}
catch (Throwable ex) {
//报告异常,但是不发送任何事件
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
2.2.2 上下文初始化prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境,包括各种变量
context.setEnvironment(environment);
//设置上下文的 bean 生成器和资源加载器
//如果application有设置beanNameGenerator、resourceLoader就将其注入到上下文中,并将转换工具也注入到上下文中
postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
applyInitializers(context);
//发布ApplicationContextInitializedEvent事件
//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 这里获取到的是BootstrapImportSelectorConfiguration这个class,而不是自己写的启动类,这个class是在之前注册的BootstrapApplicationListener的监听方法中注入的
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
load(context, sources.toArray(new Object[0]));
//发布ApplicationPreparedEvent事件
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
2.2.3 刷新上下文refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 第一步 刷新前的预处理
//记录启动时间、状态,web容器初始化其property,复制listener
prepareRefresh();
// 第二步
// 1)创建BeanFactory实例,默认实现是DefaultListableBeanFactory
// 2)解析XML中的 <bean> 为BeanDefition并注册到 BeanDefitionRegistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 第三步 BeanFactory的预准备⼯作(
//beanFactory注入一些标准组件,例如ApplicationContextAwareProcessor,ClassLoader等)
prepareBeanFactory(beanFactory);
try {
//第四步 BeanFactory准备工作完成后的后置处理工作
// 给实现类留的一个钩子,例如注入BeanPostProcessors,这里是个空方法
postProcessBeanFactory(beanFactory);
// 第五步 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
registerBeanPostProcessors(beanFactory);
// 第七步 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
initMessageSource();
// 第八步 初始化事件派发器
// bean工厂注册一个key为applicationEventMulticaster的广播器
initApplicationEventMulticaster();
// 第九步 ⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑,钩子方法,这里是个空方法
onRefresh();
// 第十步 注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
registerListeners();
// 第十一步 初始化所有剩下的⾮懒加载的单例bean
//1)初始化创建⾮懒加载⽅式的单例*Bean*实例(未设置属性)
//2)填充属性
//3) 如果bean实现了Aware相关接口 ,则调用Aware接口的实现方法
//4) 调用 BeanPostProcessor处理器的前置方法
//5)初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
//6)调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
finishBeanFactoryInitialization(beanFactory);
// 第十二步 完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件ContextRefreshedEvent
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();
}
}
}
其中,第五步 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean 是和SpringBoot自动装配相关的步骤。
就是在这一步解析的@SpringBootApplication
这个组合注解,BeanFactoryPostProcessor
比较重要的一个实现类ConfigurationClassPostProcessor
在这里调用,用来遍历BeanDefinitionRegistry中现有的BeanDefinition,解析@Import、@Configuration 、@ComponentScan等注解,将注解覆盖到的类也注册到BeanDefinitionRegistry中。