springboot项目源码_SpringBoot加载web项目流程源码解析

上一篇文章中(三分钟手写极简版SpringBoot-重复造个轮子)我们写了一个极简版的SpringBoot,基本完成了Controller的映射和访问,真实的SpringBoot是如何来做的?我们以2.3.0.RELEASE版本的SpringBoot为例来看下它是如何一步一步创建内嵌的tomcat并且注册DispatcherServlet的,它是否跟上篇文章中讲的一样,也是利用了Servlet3.0的ServletContainerInitializer和WebApplicationInitializer呢?

首先从SpringApplication#run方法开始:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  return run(new Class<?>[] { primarySource }, args);
}

继续往下走,一直到:

public ConfigurableApplicationContext run(String... args) {
  …
try {
    …
//这里会创建AnnotationConfigServletWebServerApplicationContext
    context = createApplicationContext();
    //看下这里
    refreshContext(context);
    …
  }
  …
  return context;
}
private void refreshContext(ConfigurableApplicationContext context) {
  //重点看这里
  refresh(context);
  if (this.registerShutdownHook) {
    ...
  }
}

SpringApplication#refresh:

protected void refresh(ConfigurableApplicationContext applicationContext) {
//这个就是前面创建出来的AnnotationConfigServletWebServerApplicationContext
  applicationContext.refresh();
}

这个refresh就是Spring核心的那个refresh方法。AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,因此,会调用到ServletWebServerApplicationContext#refresh:

@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
  }
catch (RuntimeException ex) {
    WebServer webServer = this.webServer;
if (webServer != null) {
      webServer.stop();
    }
throw ex;
  }
}

通过上面的代码也能很容易的看出来,里面肯定创建了webServer,默认就是tomcat,它的refresh实际上啥也没干,直接是调用的父类的refresh,也就是AbstractApplicationContext#refresh,熟悉spring的小伙伴对这个方法应该非常熟悉,这个方法就是用来初始化整个Spring的环境的,做的事情很多,我们就不再做太多介绍,里面会执行到一行onRefresh(),因为ServletWebServerApplicationContext重写了onRefresh():

@Override
protected void onRefresh() {
super.onRefresh();
try {
//就是在这里创建的webserver
    createWebServer();
  }
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
  }
}

ServletWebServerApplicationContext#createWebServer():

private void createWebServer() {
  WebServer webServer = this.webServer;
  ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
    ServletWebServerFactory factory = getWebServerFactory();
//这里真正去创建tomcat
this.webServer = factory.getWebServer(getSelfInitializer());
    。。。
  }
  。。。
}

这里首先是调用了getSelfInitializer(),然后把拿到的ServletContextInitializer传递给getWebServer()方法,注意下ServletContextInitializer,SpringBoot就是用这个接口来完成类似Servlet3.0的ServletContainerInitializer的功能的,进去到getWebServer()里面,TomcatServletWebServerFactory#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);
//添加了connector
  tomcat.getService().addConnector(connector);
//在这里设置了connector的端口号
  customizeConnector(connector);
  tomcat.setConnector(connector);
  tomcat.getHost().setAutoDeploy(false);
  configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
    tomcat.getService().addConnector(additionalConnector);
  }
//这里会把ServletContextInitializer传进去
  prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}

看下TomcatServletWebServerFactory#prepareContext():

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
  …
//这里又添加了2个ServletContextInitializer
  ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
  host.addChild(context);
//重点看下这里
  configureContext(context, initializersToUse);
  postProcessContext(context);
}

TomcatServletWebServerFactory#configureContext():

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
//这里创建了一个TomcatStarter,传递进去ServletContextInitializer
  TomcatStarter starter = new TomcatStarter(initializers);
//TomcatEmbeddedContext继承了tomcat里面的StandardContext
if (context instanceof TomcatEmbeddedContext) {
    TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
//把TomcatStarter添加到StandardContext的一个成员变量starter中
    embeddedContext.setStarter(starter);
    embeddedContext.setFailCtxIfServletStartFails(true);
  }
//把TomcatStarter添加到StandardContext的initializers中,
//initializers是一个Map
  context.addServletContainerInitializer(starter, NO_CLASSES);
  。。。
}

TomcatStarter是一个非常关键的类,它实现了ServletContainerInitializer接口,这个接口是servlet提供的,因此TomcatStarter是连接SpringBoot和tomcat的一个关键纽带。

继续往下执行就到了TomcatServletWebServerFactory#getTomcatWebServer():

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
  ….
//这里会去启动tomcat
  initialize();
}

TomcatWebServer#initialize():

private void initialize() throws WebServerException {
  logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
  。。。
// 启动tomcat
this.tomcat.start();
  //启动一个阻塞线程,防止tomcat退出
  startDaemonAwaitThread();
。。。
}

Tomcat启动以后,会执行到tomcat里面的StandardContext#startInternal():

protected synchronized void startInternal() throws LifecycleException {
  ...
// initializers是前面存进去的,里面只有一个TomcatStarter
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
    initializers.entrySet()) {
try {
//这里会调用TomcatStarter的onStartup()
//TomcatStarter里面有所有的ServletContextInitializer,因此这里就会回调到SpringBoot里面的ServletContextInitializer里面去
        entry.getKey().onStartup(entry.getValue(),
                getServletContext());
    } catch (ServletException e) {
        log.error(sm.getString("standardContext.sciFail"), e);
        ok = false;
break;
    }
  }
  ...
}

TomcatStarter#onStartup():

@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
// initializers也是前面存进去的,里面一共3个,其中一个是ServletWebServerApplicationContext里面的selfInitialize
for (ServletContextInitializer initializer : this.initializers) {
      initializer.onStartup(servletContext);
    }
  }
catch (Exception ex) {
    ...
  }
}

ServletWebServerApplicationContext#selfInitialize():

private void selfInitialize(ServletContext servletContext) throws ServletException {
  prepareWebApplicationContext(servletContext);
  registerApplicationScope(servletContext);
  WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//这里面就拿到了DispatcherServletRegistrationBean
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
    beans.onStartup(servletContext);
  }
}

首先看下ServletWebServerApplicationContext#getServletContextInitializerBeans():

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
    Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
      : Collections.singletonList(ServletContextInitializer.class);
//这里去加载DispatcherServletRegistrationBean
  addServletContextInitializerBeans(beanFactory);
  addAdaptableBeans(beanFactory);
  List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
      .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
      .collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
  logMappings(this.initializers);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,initializerType)) {
//这里就有DispatcherServletRegistrationBean
      addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
    }
  }
}

拿到了DispatcherServletRegistrationBean,上面继续执行它的onStartup(),RegistrationBean#onStartup():

@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
  //注册DispatcherServlet
  register(description, servletContext);
}

DynamicRegistrationBean# register():

@Override
protected final void register(String description, ServletContext servletContext) {
//注册DispatcherServlet
  D registration = addRegistration(description, servletContext);
if (registration == null) {
    logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
  }
//设置映射路径和启动级别
  configure(registration);
}

ServletRegistrationBean#configure():

@Override
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);
  }
}

至此,整个Spring环境和DispatcherServlet就都加载起来了。

现在还剩下一个问题,DispatcherServletRegistrationBean是如何加载到Spring的容器的呢?

在spring-boot-autoconfigure-**.jar的META-INF下面的spring.factories里面配置了一个DispatcherServletAutoConfiguration,在这里面去创建了DispatcherServletRegistrationBean。

整理下整个启动过程

(1)SpringBoot首先扫描classpath,根据里面是否有web相关的类去创建了AnnotationConfigServletWebServerApplicationContext。

(2)AnnotationConfigServletWebServerApplicationContext在它的refresh()里面回调了ServletWebServerApplicationContext的onRefresh(),在这里面去创建了tomcat。

(3)SpringBoot把实现了ServletContextInitializer接口的实现类传递到了TomcatStarter里面,TomcatStarter是由SpringBoot提供的,它同时实现了ServletContainerInitializer接口,而ServletContainerInitializer是Servlet提供的,因此,TomcatStarter是一个非常关键的纽带。

(4)当tomcat启动以后,内部会调用所有的ServletContainerInitializer的onStartup(),因此就回调到了TomcatStarter的onStartup(),进而回调到了ServletContextInitializer里面。

(5)在ServletWebServerApplicationContext里面的一个ServletContextInitializer里面去加载了DispatcherServletRegistrationBean,在它的onStartup()回调里面去创建了DispatcherServlet,并且设置了映射路径和启动级别。

(6)整个启动流程可以看出来,SpringBoot并不是像SpringMVC那样通过回调ServletContainerInitializer来完成加载。SpringBoot是直接生成了它的一个叫TomcatStarter的子类,然后在其中把对ServletContainerInitializer的回调转移到对ServletContextInitializer的回调,然后在ServletContextInitializer中去加载的DispatcherServlet。

欢迎扫码查看更多文章:

4afdf0107cb7ff799b5cc3df5a60df1f.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值