SpringBoot源码解析
SpringBoot源码解析
SpringBoot主要的强大之处,就是通过约定大于配置的方式实现了自动化导入,另外就是通过代码的方式启动tomcat省去了我们导入配置tomcat的工作。所以明白了以上这两点在SpringBoot是怎么实现的,就能够对SpringBoot的原理有一个大概的认知。
SpringApplication启动Spring
这里其实就是SpringBoot帮我们创建并刷新ApplicationContext,比较简单。并且也不是SpringBoot才会这么做,SpringMVC也有自动创建并刷新ApplicationContext的功能。
org.springframework.boot.SpringApplication#run(java.lang.String…)
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
// 创建ApplicationContext
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新ApplicationContext
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据成员属性webApplicationType,确定要启动的ApplicationContext
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
// 默认创建的就是AnnotationConfigApplicationContext
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
// 通过反射实例化ApplicationContext的实现类对象,并且强转成ConfigurableApplicationContext返回
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
SpringBoot自动化配置原理
springboot的自动化配置原理就是,扫描每个starter的META-INF/spring.factories配置文件,然后获取里面的配置类的全全路径名,然后加载成BeanDefinition交给Spring容器管理,对他们进行实例化和初始化。所以我们可以自行实现一个starter(需要引入对应的依赖jar),然后在resources下创建META-INF/spring.factories指定的配置类,配置类添加@Configuration和@ComponentScan注解,@Configuration表示他是个配置类,交给Spring管理,@ComponentScan就是告诉Spring还要扫着对应的路径下的Bean。完成后打成jar包,导入到SpringBoot
工程中,我们就可以自动注入里面的Bean。所以我们会发现我们引入了一个SpringBootStarter,我们啥配置都不需要写,就可以自动的自动注入里面的Bean。
SpringBootApplication注解的内容
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
// SpringBoot的全局开关,start生效的地方
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
EnableAutoConfiguration是一个关键性的注解,因为注解具有传递性。EnableAutoConfiguration是SpringBoot的全局开关,如果把这个注解去掉,则一切的Start都会失效,这里面就是“约定大于配置”的规则。
org.springframework.boot.autoconfigure.EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
AutoConfigurationImportSelector就是springboot自动化导入的关键,里面的selectImports方法就是处理自动化导入的地方
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
// 获取内部类的实例对象AutoConfigurationEntry
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
// 调用autoConfigurationEntry的getConfigurations,返回配置类的字符串数组
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
先看AutoConfigurationEntry 的创建
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
/*
* 这里一步非常关键,会把所有start中(jar包)META-INF/spring.factories里的配置获取出来,作为字符串list
*/
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 这里就会返回所有spring.factories配置的value
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 看loadSpringFactories方法
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 这里就是把每个starter的META-INF/spring.factories扫描出来的地方
Enumeration<URL> 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 factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
上面仅仅是获取到了所有的配置类的类路径,所以肯定有个地方调用它。
其实就是在ApplicationContext调用refresh方法时,在invokeBeanFactoryPostProcesser这一步中做的处理,invokeBeanFactoryPostProcesser是Spring容器刷新时,在BeanFactory创建完毕后,调用BeanFactory后置处理器的步骤,在Spring中实现了BeanFactoryPostProcesser的Bean都会在这里得到回调,可以对BeanFactory进行操作。会调用到一个叫做ConfigurationClassPostProcesser的BeanFactory后置处理器,顾名思义就是专门做配置类的解析处理的。就会根据每个配置类的全路径名,最后调用到loadBeanDefinitions方法,加载他们的BeanDefinition到Spring容器的BeanDefinitionMaps中。如果配置类中有@ComponentScan注解,他就会递归的解析,加载扫描路径下所有的BeanDefinition。
上两张图,从《Spring源码深度解析》里面摘取出来的

SpringBoot中Tomcat的启动
Tomcat的启动
org.springframework.boot.SpringApplication#run(java.lang.String…)
/**
* 运行 Spring application, 创建并 refreshing 一个新的
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
/*创建ApplicationContext*/
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
/*刷新ApplicationContext,看这一行*/
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
org.springframework.boot.SpringApplication#refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
/*刷新ApplicationContext*/
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
org.springframework.boot.SpringApplication#refresh
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
/**
* 强转成AbstractApplicationContext,
* 并且调用refresh方法进行刷新,
* 如果引入web-starter,
* 那么这里既是AnnotationConfigServletWebServerApplicationContext
*/
((AbstractApplicationContext)applicationContext).refresh();
}
首先调用refresh方法,肯定会走到AbstractApplicationContext的refresh方法,这是一个模板方法,已经在AbstractApplicationContext中定义好refresh时的执行流程
org.springframework.context.support.AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
// 最后会调用到这里的onRefresh方法,这里Spring留给子类ApplicationContext在容器刷新时的一个扩展点
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
SpringBoot使用的ApplicationContext是ServletWebServerApplicationContext,就是在上面SpringApplication启动这里,有一个选择ApplicationContext的步骤,普通SpringBoot的话,就会走到AnnotationConfigApplicationContext对应的分支。而当引入了web-Starter后,就会走第一个分支:AnnotationConfigServletWebServerApplicationContext。
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
/**
* 在父类AbstractApplicationContext中的refresh方法中,会预留一个空方法onRrefresh供子类实现,
* 这里就会调用到
*/
protected void onRefresh() {
super.onRefresh();
try {
// 顾名思义,创建一个web服务器
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
// 重点:获取WebServer工厂,有连个子类:TomcatServletWebServerFactory和JettyServletWebServerFactory
// 一个对应启动tomcat,一个对应启动jetty
// 这里默认是TomcatServletWebServerFactory
ServletWebServerFactory factory = this.getWebServerFactory();
// 通过TomcatServletWebServerFactory获取web服务器,保存到成员变量中
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
这里可以看到,new了一个Tomcat。
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
/**
* 创建并返回Web服务器
* @param initializers
* @return
*/
public WebServer getWebServer(ServletContextInitializer... initializers) {
/*new 一个Tomcat对象*/
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
/*通过把Tomcat对象封装成一个TomcatWebServer返回*/
return this.getTomcatWebServer(tomcat);
}
进入this.getTomcatWebServer(tomcat)
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getTomcatWebServer
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
// new了一个TomcatWebServer,并且把刚刚new的Tomca传入进去
return new TomcatWebServer(tomcat, this.getPort() >= 0);
}
org.springframework.boot.web.embedded.tomcat.TomcatWebServer#TomcatWebServer(org.apache.catalina.startup.Tomcat, boolean)
/**
* 创建一个新的{@link TomcatWebServer}实例。
* @param 底层tomcat服务器
* @param 如果服务器应该启动,则自动启动
*/
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
// 把Tomcat对象保存为当前WebServer对象的成员变量
this.tomcat = tomcat;
this.autoStart = autoStart;
// 这里就是真正把Tomcat启动起来的方法
this.initialize();
}
org.springframework.boot.web.embedded.tomcat.TomcatWebServer#initialize
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
// 启动服务器以触发初始化侦听器
this.tomcat.start();
this.rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
} catch (NamingException var5) {
}
// 不像Jetty, 所有的Tomcat线程都是守护线程。 我们创建了一个阻塞的非守护进程来停止立即关闭
// 创建一个线程,调用tomcat的await,阻塞接受请求
this.startDaemonAwaitThread();
} catch (Exception var6) {
this.stopSilently();
this.destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", var6);
}
}
}
private void startDaemonAwaitThread() {
Thread awaitThread = new Thread("container-" + containerCounter.get()) {
public void run() {
// 调用tomcat的await,阻塞接受请求
TomcatWebServer.this.tomcat.getServer().await();
}
};
awaitThread.setContextClassLoader(this.getClass().getClassLoader());
awaitThread.setDaemon(false);
// 启动线程
awaitThread.start();
}
DispatchServlet的实例化
在刚刚上面获取TomcatServletWebServerFactory的时候,Spring会执行它的生命周期。当执行到初始化回调前的调用BeanPostProcesser的postProcesserBeforeInitialization方法时,其中有一个BeanPostProcesser叫ErrorPageRegistrarBeanPostProcessor,会要求参加一个ErrorPageRegistry接口的对象,他的作用是给tomcat设置一个处理错误页面的方法。ErrorPageRegistry接口接口的唯一实现类ErrorPageCustomizer,它在实例化时要求传入一个构成方法参数DispatcherServletPath,所以要先实例化DispatcherServletPath
org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ErrorPageRegistry) {
this.postProcessBeforeInitialization((ErrorPageRegistry)bean);
}
return bean;
}
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration#errorPageCustomizer
@Bean
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {
return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);
}
但是实际上DispatcherServletPath也是一个接口,他的实现类是DispatcherServletRegistrationBean
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, this.webMvcProperties.getServlet().getPath());
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean#DispatcherServletRegistrationBean
public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
super(servlet, new String[0]);
Assert.notNull(path, "Path must not be null");
this.path = path;
super.addUrlMappings(new String[]{this.getServletUrlMapping()});
}
而DispatcherServletRegistrationBean的构造方法中,有一个我们熟悉的参数,就是DispatcherServlet,所以Spring又要先实例化DispatcherServlet
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration#dispatcherServlet
@Bean(
name = {"dispatcherServlet"}
)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setEnableLoggingRequestDetails(this.httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
所以也就是说,还没等到Tomcat创建,只是实例化ServletWebServerFactory的时候,DispatchServlet已经作为Bean交给了Spring容器管理了。最后这个Servlet就会注册给Tomcat。