前言
现在绝大多数java项目都上了Springboot框架, 因此深入理解Springboot框架的运行原理,能帮助我们更好的在Springboot框架下进行业务开发,同时能学习框架中优秀的设计思想, 本文主要是通过对Springboot源码的分析, 来理解整个springboot项目的启动流程. 因为Springboot不同版本的源码有差异, 因此特别声明, 本文是基于2.2.10.RELEASE版本进行的原理分析.
1. 导入依赖
parent>
groupId>org.springframework.boot/groupId>
artifactId>spring-boot-starter-parent/artifactId>
version>2.2.10.RELEASE/version>
/parent>
1.1 Springboot项目在spring官网已经作为顶级项目存在, 官网描述非常清楚,如果只是想搭建一个springboot项目, 只需要导入spring-boot-starter-parent, 如果是做web开发, 则还需导入spring-boot-starter-web, 本文只分析Springboot的启动流程,因此只需要导入spring-boot-starter-parent
2. 创建启动类
@SpringBootApplication
public class SpringbootApplication{
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
2.1 一个Springboot项目只需要在启动类上加@SpringBootApplicatio注解就可以, 可能很多人会有疑问,为什么加入了这个注解,就是一个springboot项目了,回答这个问题之前,我们首先要知道什么是一个Springboot项目, 接下来我会从源码角度为大家深入分析.
3. springboot启动流程图
3.1 以上是整个Springboot的run方法的执行流程, 这只是一个总体流程图, 接下里对每一步我将通过源码深入分析.
4. 实例化SpringAppliaciton
4.1 实例化SpringAppliaciton的核心是实例化了两种类型的class, 一个是ApplicationContextInitializer.class, 另一个是 ApplicationListener.class
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
4.2 getSpringFactoriesInstances该方法的核心是实例化所有依赖下META-INF/spring.factories文件里ApplicationContextInitializer和ApplicationListener类型, 并放入initializers和listeners属性中
//将META-INF/spring.factories目录下对应类型的全类名加入到set集合中
SetString> names = new LinkedHashSet
(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通过反射实例化
ListT> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
4.3 SpringFactoriesLoader.loadFactoryNames方法就是去加载META-INF/spring.factories, 请对这个方法有些印象, 因为这个方法在Springboot自动装配的时候也会调用.
public static ListString> loadFactoryNames(Class?> factoryType, @Nullable ClassLoader classLoader) {
//对应类型的全类名,这里是org.springframework.context.ApplicationContextInitializer和org.springframework.context.ApplicationListener
String factoryTypeName = factoryType.getName();
//通过key筛选对应的value集合
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static MapString, ListString>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//这个集合在web项目的源码中经常出现,它和普通的map区别在于一个key可以对应多个value
MultiValueMapString, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
//核心是加载所有类路径下META-INF/spring.factories文件
EnumerationURL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
4.4 createSpringFactoriesInstances是通过反射实例化所有的对象
private T> ListT> createSpringFactoriesInstances(ClassT> type, Class?>[] parameterTypes,
ClassLoader classLoader, Object[] args, SetString> names) {
ListT> instances = new ArrayList(names.size());
for (String name : names) {
try {
Class?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
4.5 因为是加载所有类路径下的spring.factories文件, 因此会加载spring-boot-starter-parent依赖的所有模块的spring.factories文件, 默认是加载8个initializer对象和12个listener对象.
5. 配置headless模式
5.1 headless模式springboot是默认开启的,通过System.setProperty(“java.awt.headless”,”true”)设置, 因为对于服务端而言,可能缺少显示屏、键盘或者鼠标等设备.
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
6. 获取所有的SpringApplicationRunListeners
6.1 同样是加载META-INF/spring.factories文件下所有的SpringApplicationRunListener类型的全类名,通过反射实例化.
因为我们只导入了spring-boot-starter-parent包,在子模块spring-boot下的META-INF/spring.factories文件中只有一个EventPublishingRunListener实现了SpringApplicationRunListener,因此只实例化一个SpringApplicationRunListener类型
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener
7. starting(), 发布ApplicationStartingEvent事件
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
7.1 会遍历所有的SpringApplicationRunListener类型, 因为上面我们说只有一个EventPublishingRunListener实现了SpringApplicationRunListener,因此我们进入EventPublishingRunListener的starting()
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
7.2 这里的initialMulticaster是SimpleApplicationEventMulticaster类型, 读过Spring源码的都知道, spring的时间派发器就是SimpleApplicationEventMulticaster, 通过这个对象发布事件, 因为spring事件监听是通过观察者模式实现的, 我们只需要实现ApplicationListener接口监听对应的事件就行.这里是发布了一个ApplicationStartingEvent事件, 其实在整个Springboot启动过程会发布各个节点事件, 都是通过SimpleApplicationEventMulticaster派发器派发.
8. 准备环境prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//检测对应的环境,因为我们是开发web,所以是web环境, 会示例化StandardServletEnvironment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//将所有的环境参数加载到environment对象中,这样我们就可以用environment获取配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
9. 配置是否忽略beaninfo
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
9.1 spring.beaninfo.ignore值默认是true, 表示跳过对BeanInfo类的搜索, 这些bean并不会被spring容器管理
10. 打印printBanner
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);
}
10.1 因为springboot的默认banner模式是CONSOLE, 因此会走bannerPrinter.print(environment, this.mainApplicationClass, System.out)
Banner print(Environment environment, Class?> sourceClass, PrintStream out) {
//获取banner
Banner banner = getBanner(environment);
//打印banner
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
//如果项目配置了spring.banner.image.location路径下的图片文件会被加载
banners.addIfNotNull(getImageBanner(environment));
//如果项目配置了spring.banner.location路径下的文件会被加载, 或则在类路径下添加了banner.txt文件也会被加载
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER;
}
10.2 如果图片banner和文本banner都没有配置,那么就使用默认的DEFAULT_BANNER 也就是 SpringBootBanner
@Override
public void printBanner(Environment environment, Class?> sourceClass, PrintStream printStream) {
for (String line : BANNER) {
printStream.println(line);
}
String version = SpringBootVersion.getVersion();
version = (version != null) ? " (v" + version + ")" : "";
StringBuilder padding = new StringBuilder();
while (padding.length() STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
padding.append(" ");
}
printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, version));
printStream.println();
}
}
10.3 这里的BANNER就是我们常见的springboot标志的log
. ____ _ __ _ _
/ / ___'_ __ _ _(_)_ __ __ _
( ( )___ | '_ | '_| | '_ / _` |
/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |___, | / / / /
=========|_|==============|___/=/_/_/_/
11. 创建Spring容器,createApplicationContext
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
相关推荐: Android 当系统存在多个Launcher时,如何设置开机自动进入默认的Launcher?
以设置Google Launcher2作为默认启动的Launcher为例, 其package name 为 com.android.launcher launcher activity name 为 com.android.launcher2.Launcher…