《spring源码深度解析》spring Boot

———— 2.1.1.RELEASE
words: 1.7k    views:    time: 8min

Spring Boot被设计用来简化spring应用的搭建和开发,其使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。这里主要介绍下springBoot的启动和自动化配置,以及如何集成Tomcat服务的。

示例

如果想要搭建一个类似spring mvc的web应用,可以如下:

pom.xml
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
Boot.java
1
2
3
4
5
6
@SpringBootApplication
public class Boot {
public static void main(String[] args) {
SpringApplication.run(Boot.class, args);
}
}
TestController.java
1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping("/test")
public class TestController {

@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello spring boot";
}
}

starter

上面的示例非常简洁,不过可以注意到它依赖很简单,就一个spring-boot-starter-web。starter模式的提出,使得模块的开发更加独立化,相互之间以更加松散的方式进行集成。

启动 SpringApplication

对于spring应用的启动,无非是创建spring应用上下文,然后进行容器加载,再refresh,如下代码所示,如果从前面的介绍一步步看过来,可以体会到一个层次感,BeanFactory负责Bean的创建,ApplicationContext负责BeanFactory的创建,而springMvc或者springBoot则负责了ApplicationContext的创建

org.springframework.boot.SpringApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public ConfigurableApplicationContext run(String... args) {
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);
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); // 环境变量
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
refreshContext(context); // refresh
afterRefresh(context, applicationArguments); // 空实现,留给子类扩展
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context); // 回调SpringApplicationRunListener.started
callRunners(context, applicationArguments); // 回调ApplicationRunner/CommandLineRunner
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context); // 回调SpringApplicationRunListener.running
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

这里创建的应用上下文都是基于AnnotationConfigApplicationContext的扩展,其在之前[spring. 应用上下文]中已经有过详细介绍

org.springframework.boot.SpringApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
// AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
// AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
// AnnotationConfigApplicationContext
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

创建了基于注解的上下文之后,便是对其进行一些设置以及各种事件回调,另外加载spring的各种资源配置文件,最近进行refresh

org.springframework.boot.SpringApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment); // 设置环境变量
postProcessApplicationContext(context); // 对ApplicationContext进行一些设置
applyInitializers(context); // 回调ApplicationContextInitializer.initialize
listeners.contextPrepared(context); // 回调SpringApplicationRunListener.contextPrepared
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}

// 硬编码注册几个bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) { // 运行BeanDefinition覆盖
((DefaultListableBeanFactory)beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}

// 加载spring资源文件
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context); // 回调SpringApplicationRunListener.contextLoaded
}

这里对于容器的加载统一交给了BeanDefinitionLoader

org.springframework.boot.SpringApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader =
createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);

if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}

protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
return new BeanDefinitionLoader(registry, sources);
}

BeanDefinitionLoader则将加载又委托给了AnnotatedBeanDefinitionReaderXmlBeanDefinitionReader,具体分别参考前面的笔记:[spring. BeanDefinition解析(XmlBeanDefinitionReader)]以及[spring. 应用上下文(ApplicationContext)],这里不再赘述

org.springframework.boot.BeanDefinitionLoader
1
2
3
4
5
6
7
8
9
10
11
12
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

自动化配置 @SpringBootApplication

上面的分析基本描述了springBoot启动时对于容器的创建和加载,但是对于各个starter模块是如何加载的呢,这里就从注解@SpringBootApplication入手

org.springframework.boot.autoconfigure.SpringBootApplication
1
2
3
4
5
6
7
8
9
10
@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

由于注解的传递性,再跟到注解@EnableAutoConfiguration,而其又使用@Import进行了声明,关于@Import在之前[spring. 应用上下文(ApplicationContext)]已经做过详细介绍,这里看下AutoConfigurationImportSelector中具体的处理

org.springframework.boot.autoconfigure.EnableAutoConfiguration
1
2
3
4
5
6
7
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

AutoConfigurationImportSelector

关于ImportSelector的调用流程,之前已经介绍过,这里从selectImports作为入口

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

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;
}

最终对于starter的加载交给SpringFactoriesLoader,其通过硬编码的方式加载所有文件:META-INF/spring.factories,剩下的就与之前一样交给ConfigurationClassPostProcessor处理了

org.springframework.core.io.support.SpringFactoriesLoader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}

try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : // META-INF/spring.factories
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // META-INF/spring.factories
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

Tomcat启动

结合之前spring应用上下文的介绍,其在模板方法refresh中提供了onRefresh()供子类扩展,而springBoot正是在onRefresh()进行了web容器的初始化。

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory(); // 对Tomcat、Jetty、Undertow的初始化进行了统一抽象
this.webServer = factory.getWebServer(getSelfInitializer());
}else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}

然后在finishRefresh()中进行启动

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}

private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.start();
}
return webServer;
}


参考:

  1. 《spring源码深度解析》 郝佳