文章目录
- SpringBoot原理讲解
- 一、SpringBoot是什么?
- 二、SpringBoot自动装配原理?
- 三、SpringBoot配置文件加载顺序?
- 四、SpringBoot是如何解析yaml、properties文件?以及如何自定义解析配置文件?
- 解析yaml、properties文件
- 自定义解析文件
- 五、SpringBoot启动顺序?
- 刷新上下文
SpringBoot原理讲解
一、SpringBoot是什么?
Spring Boot是Spring公司的一个顶级项目,和Spring Framework是一个级别的。
Spring Boot实际上是利用Spring Framework 自动配置特性完成。编写项目时不需要编写xml文件。发展到现在,Spring Boot已经具有很很大的生态圈,各种主流技术已经都提供了Spring Boot的启动器。
Spring Boot 利用Spring Framework 自动配置特性完成,多数xml不需要配置,整合其他技术,使用起来更加方便,组装完整环境的一个开发平台
Spring Boot的出现使我们能够将侧重点放在开发上。
Spring Boot启动器帮我们解决了繁琐的导jar包的时间,以及jar包之间存在的冲突问题
在Spring Boot 还没出现前,我们开发时需要对项目环境搭建配置繁琐的xml文件。SpringBoot的自动装配恰好解决了这问题。
SpringBoot的出现让部署web项目变得相对简单。
二、SpringBoot自动装配原理?
我们都知道要启动Springboot应用,在SpringBoot启动类上添加**@SpringBootApplication注解**
那么@SpringBootApplication注解跟SpringBoot的自动装配密切相关。
@SpringBootApplication注解下有几个重要注解:
- @SpringBootConfiguration 实际就是上封装了@Configuration注解
- @EnableAutoConfiguration 开启自动装配
- @ComponentScan 组件扫秒
这里重点关注@EnableAutoConfiguration 注解,那么@EnableAutoConfiguration 下有注解
有个注解@Import 注解上声明类AutoConfigurationImportSelector.class
我们点进去 找到selectImports(AnnotationMetadata annotationMetadata) 方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//加载jar中/META-INF/spring.factores配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);//去除重复配置类信息
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
//加载Factories
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//加载Factories
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
....
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
....
}
catch (IOException ex) {
....
}
return result;
}
三、SpringBoot配置文件加载顺序?
关于配置文件加载顺序具体可看实现类:
- org.springframework.boot.context.config.ConfigFileApplicationListener弃用
- org.springframework.boot.context.config.ConfigDataEnvironment
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
//初始化文件加载顺序
static {
List<ConfigDataLocation> locations = new ArrayList<>();
locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
}
四、SpringBoot是如何解析yaml、properties文件?以及如何自定义解析配置文件?
解析yaml、properties文件
首先SpringBoot启动时候会加载 SpringBoot包下 META-INF/spring.factories,其中跟加载解析文件相关的类
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader
然后通过springboot 监听器监听事件***EnvironmentPostProcessorApplicationListener***
调用***ConfigDataLoaders.load()***方法一层一层的调用方法, 最终调用到***PropertySourceLoader.load()***方法
调用过程
- ConfigDataImporter.resolveAndLoad(…)
- ConfigDataLoaders.load()
- YamlPropertySourceLoader.load(…)或PropertiesPropertySourceLoader.load(…)
自定义解析文件
- 创建类实现接口 PropertySourceLoader
package com.any;
public class JSONPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[]{"json"};
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if(resource == null || !resource.exists()){
return Collections.emptyList();
}
Map<String, Object> configs = JSON.parseObject(resource.getInputStream(), Map.class);
return Collections.singletonList(
new MapPropertySource(name, configs)
);
}
- 创建spring.factories文件
org.springframework.boot.env.PropertySourceLoader=\
com.any.JSONPropertySourceLoader #指定SourceLoader类
- 编写配置文件application.json
{
"spring.application.name": "anywhere"
}
五、SpringBoot启动顺序?
- 开启计时器 stopWatch.start()
- 开启监听器 listeners.starting
- 初始化配置文件 prepareEnvironment
- 打印Banner printBanner(environment)
- 创建上下文 createApplicationContext()
- 刷新上下文 refreshContext(context)
- 停止计时器 stopWatch.stop()
- 启动完成
- listeners.started(context);
- listeners.running(context)
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); //创建计时器,记录一些启动信息
stopWatch.start();//开始计时
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass); //开启监听器
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);//初始化配置并绑定到环境
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);//打印Banner
context = createApplicationContext();//创建上下文
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//为刷新上下前做准备
refreshContext(context);//刷新上下文
afterRefresh(context, applicationArguments);
stopWatch.stop();//停止计时
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
....
}
....
try {
listeners.running(context); //启动完成通知
}
....
return context;
}
刷新上下文
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 为刷新上下文做准备
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 配置BeanFactory
prepareBeanFactory(beanFactory);
try {
......
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 实例化和调用所有已注册的Bean
invokeBeanFactoryPostProcessors(beanFactory);
//实例化和注册所有Bean
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化此上下文的消息源。
initMessageSource();
// 初始化此上下文的事件多播程序
initApplicationEventMulticaster();
// 初始化一些与上下文有特别关系的bean对象(如创建tomcat)
onRefresh();
// 检查监听器bean并注册
registerListeners();
// 完成此上下文bean工厂的初始化,初始化所有剩余的非懒汉式单例bean
finishBeanFactoryInitialization(beanFactory);
// 通知完成此上下文的刷新
finishRefresh();
}
catch (BeansException ex) {
....
}
finally {
....
}
}
}