一、Spring Boot自动装配源码分析
首先主启动类上写上@SpringBootApplication。
@SpringBootApplication
public class SpringSourceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSourceApplication.class, args);
}
}
这个注解做了什么?
@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这个注解,看名字我们大胆猜测就是开启自动配置。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
这个注解上又有两个核心的注解:@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)。
下面我们分别看看它们做了什么?
@AutoConfigurationPackage注解导入了AutoConfigurationPackages.Registrar.class。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
Registrar 实现了ImportBeanDefinitionRegistrar接口,则在解析配置类时,会调用registerBeanDefinitions方法。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//注册beandefinition
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
debug跟踪下,
PackageImports(AnnotationMetadata metadata) {
//获取@AutoConfigurationPackage的属性"basePackageClasses" 和"basePackages"
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
//获取的包名为空
if (packageNames.isEmpty()) {
//获得主启动类的包名
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
//赋值给packageNames属性
this.packageNames = Collections.unmodifiableList(packageNames);
}
注册BeanDefinition,beanName是org.springframework.boot.autoconfigure.AutoConfigurationPackages,设置它的basePackages是我们的包名。
@AutoConfigurationPackage注解的主要作用就是将主程序类所在包及所有子包下的组件到扫描到spring容器中。
重点是@EnableAutoConfiguration注解上import了另一个AutoConfigurationImportSelector。
AutoConfigurationImportSelector实现DeferredImportSelector接口。
在解析配置@Import注解时,如果实现了DeferredImportSelector,则会调用 getImportGroup()方法,返回Class,再调用它的process方法。
我们这里getImportGroup()方法
调用链路:
refresh
invokeBeanFactoryPostProcessors(beanFactory);
invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(registry)
ConfigurationClassPostProcessor.processConfigBeanDefinitions
parser.parse(candidates)
doProcessConfigurationClass
processImports(configClass, sourceClass, getImports(sourceClass), true)
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
this.deferredImportSelectors.add(holder);
this.deferredImportSelectorHandler.process()
register(DeferredImportSelectorHolder deferredImport)
deferredImport.getImportSelector().getImportGroup(); //这里就调用了AutoConfigurationImportSelector的 getImportGroup()方法,返回AutoConfigurationGroup.class
handler.processGroupImports();
grouping.getImports() //这个里面的两个核心方法执行完,就获得了走动配置类的信息,后面再循环,继续递归处理调用processImports
this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector())//调用AutoConfigurationImportSelector的process方法
this.group.selectImports();
//加载BeanDefinitions
this.reader.loadBeanDefinitions(configClasses);
getImportGroup方法返回AutoConfigurationGroup.class
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
那么程序会调用AutoConfigurationGroup.process方法:
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//获取自动配置entry
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
getAutoConfigurationEntry方法
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);
//获取需要排除,@EnableAutoConfiguration配置的exclude和excludeName;和spring.autoconfigure.exclude属性配置的
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//排除
configurations.removeAll(exclusions);
//获取自动配置类,key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的Condition,然后调用match方法,留下按有效的
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
获取自动配置类方法getCandidateConfigurations
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;
}
获取要获取配置文件的key,这里是org.springframework.boot.autoconfigure.EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// factoryTypeName是org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories方法,加载配置文件内容
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//先从缓存中取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//FACTORIES_RESOURCE_LOCATION是 META-INF/spring.factories,加载所有jar包里的这个文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
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 factoryTypeName = ((String) entry.getKey()).trim();
//逗号分隔value
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
//放入缓存
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
最后回调selectImports方法
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
总结:
其实就是import了AutoConfigurationImportSelector,而AutoConfigurationImportSelector实现DeferredImportSelector接口。
加载所有jar包里的META-INF/spring.factories文件。
二、Spring Boot启动流程源码分析
带着两个疑问看下Spring Boot启动流程源码
1、什么时候启动的内置tomcat?
2、什么时候注册的DispatcherServlet?
在META-INF/spring.factories的自动配置类里有两个与其相关的自动配置类DispatcherServletAutoConfiguration和ServletWebServerFactoryAutoConfiguration。
首先看下这个ServletWebServerFactoryAutoConfiguration 类。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
...
}
首先导入了ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar类,该类实现ImportBeanDefinitionRegistrar接口,所以ConfigurationClassPostProcessor解析配置类的时候,自动调用该接口的registerBeanDefinitions方法。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
这里的BeanPostProcessorsRegistrar在该方法中注册了两个bean的定义信息:一个是ErrorPageRegistrarBeanPostProcessor,另一个是注册的bean定义信息是WebServerFactoryCustomizerBeanPostProcessor,主要是WebServerFactoryCustomizerBeanPostProcessor。
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//在创建bean的初始化前执行,只拦截WebServerFactory的bean
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//获得WebServerFactoryCustomizer,执行customizer的customize方法
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
}
作用是对WebServerFactory工厂做一些定制化的工作(配置)。
再回来看ServletWebServerFactoryAutoConfiguration,还导入了其他三个组件:
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class
ServletWebServerFactoryConfiguration.EmbeddedJetty.class
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class
默认情况,只有EmbeddedTomcat会生效。导入了一个TomcatServletWebServerFactory类型的组件。后面tomcat的创建就靠它。
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
现在我们再看看DispatcherServletAutoConfiguration配置类
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)//ServletWebServerFactoryAutoConfiguration自动配置后再处理
public class DispatcherServletAutoConfiguration {
//容器中的beanName
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
//向ioc容器注入DispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
//向ioc容器注入DispatcherServletRegistrationBean
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
...
}
向容器中导入了DispatcherServlet 和DispatcherServletRegistrationBean 的组件。
DispatcherServletRegistrationBean 实现org.springframework.boot.web.servlet.ServletContextInitializer接口,这个接口不像WebApplicationInitializer一样被SpringServletContainerInitializer自动探测,因此不会自动被servlet容器启动。但是它仍有着被spring管理的生命周期。
下面我们跟踪下代码,从头梳理下启动过程:
主启动类:
@SpringBootApplication
public class SpringbootSourceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSourceApplication.class, args);
}
}
调用SpringApplication.run启动springboot应用
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//primarySources 是主启动类
return new SpringApplication(primarySources).run(args);
}
实例化SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
//主启动类不能为空
Assert.notNull(primarySources, "PrimarySources must not be null");
//将启动类放入primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//根据main方法推算出mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
根据classpath 下的类,推算当前web应用类型,我们的是WebApplicationType.SERVLET
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
下面看看 run()方法
public ConfigurableApplicationContext run(String... args) {
//用来记录当前springboot启动耗时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//开启了Headless模式:
configureHeadlessProperty();
//去spring.factroies中读取了SpringApplicationRunListener 的组件, 就是用来发布事件或者运行监听器,默认就一个org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布ApplicationStartingEvent事件,在运行开始时发送
listeners.starting();
try {
//根据命令行参数 实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//忽略beaninfo的bean
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//根据webApplicationType创建Spring上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//预初始化spring上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 加载spring ioc 容器 **相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
// 加载自动配置类:invokeBeanFactoryPostProcessors , 创建servlet容器onRefresh
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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;
}
解析参数:
public ConfigurableApplicationContext run(String... args) {
//用来记录当前springboot启动耗时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//开启了Headless模式:
configureHeadlessProperty();
//去spring.factroies中读取了SpringApplicationRunListener 的组件, 就是用来发布事件或者运行监听器,默认就一个org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布ApplicationStartingEvent事件,在运行开始时发送
listeners.starting();
try {
//根据命令行参数 实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//忽略beaninfo的bean
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//根据webApplicationType创建Spring上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//预初始化spring上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 加载spring ioc 容器 **相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
// 加载自动配置类:invokeBeanFactoryPostProcessors , 创建servlet容器onRefresh
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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;
}
预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据webApplicationType,创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境命令行参数
configureEnvironment(environment, applicationArguments.getSourceArgs());
//配置激活的环境
ConfigurationPropertySources.attach(environment);
//发布ApplicationEnvironmentPreparedEvent事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
根据webApplicationType创建Spring上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
// 我们的是 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
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) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
//反射生成上下文对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
创建上Spring 上下文,还没有刷新,这两个方法,看过spring源码的就比较熟悉
public AnnotationConfigServletWebServerApplicationContext() {
//创建注解的BeanDefinition读取器,注册一些处理注解的处理器BeanDefinition
this.reader = new AnnotatedBeanDefinitionReader(this);
//创建bean定义扫描器,设置要扫描的注解@Component
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
预初始化spring上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
//拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
applyInitializers(context);
//发布ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// / 获取当前spring上下文beanFactory (负责创建bean)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
// 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//设置当前spring容器是不是要将所有的bean设置为懒加载
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类 (因为后续要根据配置类解析配置的所有bean)
load(context, sources.toArray(new Object[0]));
//发布ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
下面看下refreshContext,这个会调用spring上下文的刷新方法
private void refreshContext(ConfigurableApplicationContext context) {
//spring 的refresh方法
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
refresh的方法,就不展开了,最终会调用 onRefresh()方法:
@Override
protected void onRefresh() {
super.onRefresh();
try {
//创建WebServer
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
这里创建web服务器。
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//获取ServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
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();
}
getWebServerFactory()方法
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
第一步:从容器中获取ServletWebServerFactory组件名,默认情况下,只有在ServletWebServerFactoryAutoConfiguration自动配置的TomcatServletWebServerFactory。
第二步:校验组件名,只有一个组件名的时候,不会抛出异常,如果找不到组件或者组件有多个都会抛出ApplicationContextException。
第三步:从容器中获取到ServletWebServerFactory组件,默认就是TomcatServletWebServerFactory。
下面看下this.webServer = factory.getWebServer(getSelfInitializer())方法
先看getSelfInitializer(),这个方法比较重要:内部是一个selfInitialize回调方法,并不会立马执行。
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
回调的selfInitialize方法,它是一个函数式接口
//同时这个selfInitialize方法不会立刻调用,只是作为一个回调方法,当ServletContextInitializer.onStartUp调用时,才会被调用
private void selfInitialize(ServletContext servletContext) throws ServletException {
//准备WebApplicationContext,主要做一些校验和设置属性
prepareWebApplicationContext(servletContext);
//设置ApplicationScope
registerApplicationScope(servletContext);
//注册一些关于Servlet环境的组件
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//getServletContextInitializerBeans(),主要是创建了一个ServletContextInitializerBeans集合
//对ServletContextInitializerBeans集合遍历,是对它内部的sortedList进行遍历,里面存放了容器中的所有ServletContextInitializer
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//调用所有的ServletContextInitializer的onStartup方法
beans.onStartup(servletContext);
}
}
再看getWebServer方法:
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//创建tomcat
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
//准备Context
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
看prepareContext方法:
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
//创建TomcatEmbeddedContext
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
//设置一些基本属性
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
context.setUseRelativeRedirects(false);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldSkipPatterns(context);
//设置WebappLoader
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
//增加LifecycleListener
context.addLifecycleListener(new StaticResourceConfigurer(context));
//合并Initializers,主要是添加一些其他的ServletContextInitializer
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
//配置context
configureContext(context, initializersToUse);
postProcessContext(context);
}
进入configureContext方法:
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
//创建了TomcatStarter,这个实现了ServletContainerInitializer接口
//对servlet3.0规范熟悉的知道,tomcat在servlet容器初始化的时候,会调用所有的ServletContainerInitializer接口的onStartup方法
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
//添加到context中
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
//添加tomcat阀门
for (Valve valve : this.contextValves) {
context.getPipeline().addValve(valve);
}
//错误页配置
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath());
tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
for (MimeMappings.Mapping mapping : getMimeMappings()) {
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
configureSession(context);
//对TomcatContext进行自定义配置
new DisableReferenceClearingContextCustomizer().customize(context);
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
解释一下在getWebServer方法截至运行到prepareContext,springboot究竟做了哪些事情:
1、getSelfInitializer返回的是一个函数式接口,不会立即执行,内部主要是拿到springboot中的所有ServletContextInitializer,并依次执行它的onStartup方法
2、把这个函数式接口作为参数传入到getWebServer方法中
3、getWebServer方法主要做了以下几件事
3.1、创建了一个Tomcat实例,设置了一些属性
3.2、为tomcat的Host配置Context
3.3、创建了一个TomcatStarter并把上面的getSelfInitializer返回的函数式接口设置到它的成员变量中,TomcatStarter实际上是javax.servlet.ServletContainerInitializer,tomcat在servlet容器初始化的时候,会调用所有的ServletContainerInitializer接口的onStartup方法
3.4、然后把TomcatStarter添加到context中
继续看getTomcatWebServer(tomcat)代码,直接new了一个TomcatWebServer,并把前面创建的tomcat传进去。
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
继续走调用initialize方法
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
initialize方法如下:终于在这一步,看到了tomcat.strart()方法,至此,tomcat终于启动。
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
// 启动我们的tomcat
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
tomcat.start()方法启动后,因为TomcatStarter实现了ServletContainerInitializer接口,在前面手动调用了context.addServletContainerInitializer(starter, NO_CLASSES);,把tomcatStart添加到context中,所以这里的TomcatStarter并不是通过SPI机制加载到tomcat中,这里与springmvc处理不一样。
tomcat启动后,会执行ServletContainerInitializer的onStartUp方法,所以TomcatStarter会被调用
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
+ ex.getMessage());
}
}
}
这里的this.initializers有三个,我们只要管前面传进来的getSelfInitializer()方法:这里前面提到的selfInitialize方法才会被调用
private void selfInitialize(ServletContext servletContext) throws ServletException {
//准备WebApplicationContext,主要做一些校验和设置属性
prepareWebApplicationContext(servletContext);
//设置ApplicationScope
registerApplicationScope(servletContext);
//注册一些关于Servlet环境的组件
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//getServletContextInitializerBeans(),主要是创建了一个ServletContextInitializerBeans集合
//对ServletContextInitializerBeans集合遍历,是对它内部的sortedList进行遍历,里面存放了容器中的所有ServletContextInitializer
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//调用所有的ServletContextInitializer的onStartup方法
beans.onStartup(servletContext);
}
}
这里getServletContextInitializerBeans()在容器中查找 org.springframework.boot.web.servlet.ServletContextInitializer,此时返回了四个
其中第一个,就是配置类中配置的DispatcherServletRegistrationBean,而springboot通过该类注册了DispatcherServlet,我们接下来来看tomcat是怎么注册的。
进入DispatcherServletRegistrationBean的onstart方法,直接进到它的父类RegistrationBean
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
//注册
register(description, servletContext);
}
进入register方法
protected final void register(String description, ServletContext servletContext) {
//执行addRegistration
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
//调用configure
configure(registration);
}
addRegistration方法
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
//注册DispatcherServlet
return servletContext.addServlet(name, this.servlet);
}
这里this.servlet也就是DispatcherServlet,是在前面DispatcherServletAutoConfiguration自动配置类中传入到DispatcherServletRegistrationBean类中的。
走进configure方法:这里会给ServletRegistration.Dynamic设置一些属性,这也是javax.servlet的原生方法
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}
至此,tomcat已经启动了,DispatcherServlet也已经注册到tomcat中。