入口
@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 {
// ...
}
这个注解就是三个常用在一起的注解@SpringBootConfiguration,@EnableAutoConfiguration以及@ComponentScan的组合。
@SpringBootConfiguration
这个注解实际上和@Configuration有相同的作用,配备了该注解的类就能够以JavaConfig的方式完成一些配置,可以不再使用XML配置。
@ComponentScan
这个注解完成的是自动扫描的功能,相当于Spring XML配置文件中的:
<context:component-scan>
@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
重点关注@Import(EnableAutoConfigurationImportSelector.class
EnableAutoConfigurationImportSelector
在这个类中,提供了一个getCandidateConfigurations()
方法用来加载配置文件。借助Spring提供的工具类SpringFactories的loadFactoryNames()方法加载配置文件。扫描的默认路径位于META-INF/spring.factories
中。
启动流程
创建SpringApplication实例
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
//spring applicationContext
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
SpringApplication的构造函数
跟进SpringApplication.run(),调用了SpringApplication的构造函数,这个构造函数的主要作用是创建一个SpringApplication实例,applicationContext将加载指定的主要来源提供的bean。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//设置资源加载器
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置主要资源类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断当前web应用类型(NONE/SERVLET/REACTIVE),默认为SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置将应用于Spring的ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//设置将应用于SpringApplication的ApplicationListener(监听器)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断并设置主类
this.mainApplicationClass = deduceMainApplicationClass();
}
配置source
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
推断web应用
WebApplicationType是一个枚举,deduceFromClasspath方法:
static WebApplicationType deduceFromClasspath() {
// 类路径中是否包含DispatcherHandler,且不包含DispatcherServlet,也不包含ServletContainer,返回基于reactive的web应用
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 如果Servlet或者ConfigurableWebApplicationContext不存在,返回非web应用
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
//返回基于servlet的web应用
return WebApplicationType.SERVLET;
}
创建初始化构造器
setInitializers()
方法
getSpringFactoriesInstances()
getSpringFactoriesInstances(ApplicationContextInitializer.class)
将会调用org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
从META-INF/spring.factories配置文件中找到所有ApplicationContextInitializer接口对应的实现类配置,然后通过反射机制构造出对应的实例对象
spring.factories
文件
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=
# Application Listeners
org.springframework.context.ApplicationListener=
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=
设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
也是通过getSpringFactoriesInstances()
方法在spring.factories配置文件中找到所有ApplicationListener接口对应的实现类配置
配置应用main方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
run方法分析
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
//Spring计时器StopWatch
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 创建并配置Environment(这个过程会加载application配置文件)
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//根据应用类型创建对应的Context容器
context = createApplicationContext();
//配置启动错误回调
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 刷新Context容器前置处理
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新context容器
refreshContext(context);
//刷新Context容器后置处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// Context容器refresh完毕发布
//即ApplicationRunner,CommandLineRunner
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;
}
应用启动监听器模块
SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
创建并配置Environment
Environment
Environment的组成
-
profiles
通常使用profiles划分多环境,每个环境会有有些bean不同、配置不同等
-
properties
在java中properties代表着key-value的键值对象集合。Environment内部设计了key-value结构的对象来存储相应的键值。
Environment创建及配置过程
prepareEnvironment
- prepareEnvironment方法创建一个Environment实例,也就是spring的运行环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置environment
configureEnvironment(environment, applicationArguments.getSourceArgs());
//环境准备,触发监听器
listeners.environmentPrepared(environment);
//绑定到SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
listeners.environmentPrepared(environment)
方法触发监听器EventPublishingRunListener
添加spring.factories中配置的ApplicationListener
(包括ConfigFileApplicationListener
,构造SpringApplication时调用setListeners()
载入),随后再触发ConfigFileApplicationListener
,ConfigFileApplicationListener将会加载如application.properties/yml这样的配置文件
- getOrCreateEnvironment
创建environment对象,根据web容器类型构造,由上文中的deduceFromClassPath推断的类型
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
//添加、删除、重排序配置源(@PropertySource、@ImportResource)
configurePropertySources(environment, args);
//配置profiles
configureProfiles(environment, args);
}
- ConfigFileApplicationListener
ConfigFileApplicationListener
处理逻辑,最后调用new Loader(environment, resourceLoader).load()
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 只触发Environment相关的事件
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(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());
}
}
- Loader
Loader
构造器
构造方法中将会从spring.factories中加载PropertySourceLoader接口的具体实现类,有两个实现,是YamlPropertySourceLoader
(ym、yaml)和PropertiesPropertySourceLoader
(property、xml)
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
// 文件application配置文件的资源加载器,包括propertis/xml/yml/yaml扩展名
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
load()
方法
//第一个load方法
public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 初始化profiles
initializeProfiles();
while (!this.profiles.isEmpty()) {
// 消费一个profile
Profile profile = this.profiles.poll();
// active的profile添加到Environment
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
// 加载
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
// 重置Environment中的profiles
resetEnvironmentProfiles(this.processedProfiles);
// 加载
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
// 添加所有properties到Environment中
addLoadedPropertySources();
}
//第二个load方法
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// 获取并遍历所有待搜索的位置,没有自定义一些搜索位置,那么默认搜索classpath:/、classpath:/config/、file:./、file:./
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
// 获取所有待加载的配置文件名,没有自定义则返回默认的配置文件名application
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
// 加载每个位置的每个文件
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
//第三个load方法
//主要逻辑表明将会加载每个加载器可以支持的配置文件,在Loader初始化的时候我们获得了两个加载器,同时每个加载器支持两种格式
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
...
Set<String> processed = new HashSet<>();
// 遍历加载器
for (PropertySourceLoader loader : this.propertySourceLoaders) {
// 获取扩展名
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
// 加载对应扩展名的文件
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
//
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
// 当前没有profile
if (profile != null) {
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// 加载具体格式的文件
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
//第四个load方法
private void load(
PropertySourceLoader loader,
String location,
Profile profile,
DocumentFilter filter,
DocumentConsumer consumer
) {
try {
// 获取资源
Resource resource = this.resourceLoader.getResource(location);
// 加载为Document对象
List<Document> documents = loadDocuments(loader, name, resource);
List<Document> loaded = new ArrayList<>();
// 遍历Document集合
for (Document document : documents) {
if (filter.match(document)) {
// 添加profile
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
// 回调处理每个document
loaded.forEach((document) -> consumer.accept(profile, document));
}
} catch (Exception ex) {}
}
//第一个load调用
private void addLoadedPropertySources() {
MutablePropertySources destination = this.environment.getPropertySources();
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
// 反向排序
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = new HashSet<>();
// 遍历loaded
for (MutablePropertySources sources : loaded) {
// 遍历配置
for (PropertySource<?> source : sources) {
// 排重
if (added.add(source.getName())) {
// 添加每个到Environment中
addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}
加载默认的application.properties/yml
private void initializeProfiles() {
// 第一个profile为null,这样能保证首个加载application.properties/yml
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
addActiveProfiles(activatedViaProperty);
// 没有额外配置profile的时候,将使用默认的
if (this.profiles.size() == 1) {
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
扩展知识:Spring Cloud Config及其他配置中心,配置刷新使用springboot监听机制
打印banner
Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null)
? this.resourceLoader : new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
//控制banner展示
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
//没有配置则打印SpringBootBanner.BANNER
创建Context容器
createApplicationContext()
方法会先获取显式设置的应用上下文(applicationContextClass),如果不存在,再加载默认的环境配置(通过是否是web environment判断),默认选择AnnotationConfigApplicationContext注解上下文(通过扫描所有注解类来加载bean),最后通过BeanUtils实例化上下文对象,并返回。
ConfigurableApplicationContext继承的两个方向:
- LifeCycle:生命周期类,定义了start启动、stop结束、isRunning是否运行中等生命周期空值方法
- ApplicationContext:应用上下文类,其主要继承了beanFactory(bean的工厂类)
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_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) {
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
Context容器前置处理
prepareContext()
方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联。加载过程先填充Environment
以及设置的参数,然后执行注册到spring.factories
的ApplicationContextInitializer
切面,如果自己实现ApplicationContextInitializer的话要注意这时context已经有的信息是什么。接着发布ApplicationContextInitializedEvent
事件,然后加载bean,最后发布ApplicationPreparedEvent
事件。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将环境和上下文关联起来
context.setEnvironment(environment);
// 为上下文配置Bean生成器以及资源加载器(如果它们非空)
postProcessApplicationContext(context);
// 调用初始化器
applyInitializers(context);
// 触发Spring Boot启动过程的contextPrepared事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载
load(context, sources.toArray(new Object[sources.size()]));
// 触发Spring Boot启动过程的contextLoaded事件
listeners.contextLoaded(context);
}
刷新Context容器
refreshContext(context)
方法将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//记录启动时间、状态,web容器初始化其property,复制listener
prepareRefresh();
// 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//beanFactory注入一些标准组件,例如ApplicationContextAwareProcessor,ClassLoader等
prepareBeanFactory(beanFactory);
try {
//允许在context子类中对bean工厂进行后处理。即给实现类留的一个钩子,例如注入BeanPostProcessors,这里是个空方法
postProcessBeanFactory(beanFactory);
// 调用在context注册为bean的工厂处理器。这步注册定义的bean
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截Bean创建的Bean处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// 为此上下文初始化事件广播器。
initApplicationEventMulticaster();
// 给实现类留的一钩子,可以执行其他refresh的工作,这里是个空方法
onRefresh();
// 将listener注册到广播器中
registerListeners();
// 实例化未实例化的bean
finishBeanFactoryInitialization(beanFactory);
//清理缓存,注入DefaultLifecycleProcessor,发布ContextRefreshedEvent
finishRefresh();
}
catch (BeansException 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();
}
}
}
刷新Context后置处理
afterRefresh(context, applicationArguments)
方法是个空实现
Runner
ApplicationRunner和CommandLineRunner是springboot启动加载类
callRunners方法中调用了ApplicationRunner和CommandLineRunner的run方法
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);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
监听器模块启动
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}