SpringMVC中整合tomcat和spring原理

703 阅读3分钟

Demo搭建

springboot用多了很多东西都自动配置,其中的原理都不太了解,这次不用springboot用最基本的springmvcdemo来探寻springmvc其中的原理

image.png 根据官网提供的demo搭建了一个最简单的springMVCdemo。

SPI机制

这个demo的第一个问题是,为什么写了一个继承于WebApplicationInitializer的类就能被tomcat给自动加载到。那么我们就得先讲一下什么是SPI机制,要说SPI又得先说一个java的类ServiceLoader该类的作用是传入指定接口后回加载当前系统中所有实现类 image.png 身为实现类,想要被ServiceLoader加载到的方法是在META-INF/services中创建一个名字是接口全路径的文件,文件内容是自己实现类的全路径 image.png

SpringMVC中的SPI机制

在了解了SPI的原理之后,我们来看springMVC中的SPI应用 image.png 从图上我们可以看到springmvc实现了SPI让tomcat能加载到SpringServletContainerInitializer

image.png 然后根据JSR的另一个协议,传过来的全部都是指定好的类 image.png 而这个类就是我们自定义的类实现的类,所以我们自定义的类的方法才被执行。

SpringMVC启动流程

知道了springMVC是如何能被tomcat加载之后,我们来看springMVC的启动流程。

我们再来看一下我们写的springMVC初始化代码

public void onStartup(ServletContext servletContext) throws ServletException {
    // Load Spring web application configuration
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(AppConfig.class);

    // Create and register the DispatcherServlet
    DispatcherServlet servlet = new DispatcherServlet(context);
    ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
    registration.setLoadOnStartup(1);
    registration.addMapping("/");
}

我们创建了一个IOC容器,然后传递给了 DispatcherServlet 然后让tomcat加载了这个servlet。我们就先来研究这个servlet image.png 从继承图可以看出这个servlet经过了tomcat和spring的封装。可以从servlet顶层的生命周期开始看代码

image.png

当一个servlet被初始化的时候会调用该servlet的init方法,当调用方法的时候会调用service方法。

DispatcherServlet的init流程

image.png 老套的模板方法,一层一层继承下来,最后是在 FrameworkServlet类中执行了IOC容器的刷新方法。我们只需要关注 FrameworkServlet里面干的事情,重点来看一下 initWebApplicationContext方法 image.png 我们看到在方法中有给SpringMVC添加父容器的这么一个动作。我们很自然的想到了Spring的容器和SpringMVC容器整合的操作,SpringMVC只扫描controller的bean,然后Spring扫描service,dao的bean。整合的原理的就是这个父子容器。

SpringMVC的父子容器启动过程

在spring官网有这么一张图,表示了SpringMVC和Spring的关系

image.png 现在使用spring和springMVC整合的demo来看看是整合的原理是什么 image.png 使用一个新的继承类AbstractAnnotationConfigDispatcherServletInitializer,我们研究这个类就可以了解springMVC整合spring的流程 image.png 首先还是从onStartUp方法开始,在父类AbstractDispatcherServletInitializer中写了方法 image.png 可以看到首先调用了super的onStartUp,我们先看上面的流程 image.png 父类创建了一个IOC容器并且放到了一个监听器里面 image.png 创建IOC容器的配置也是交给我们自定义实现的配置类。看完父类的逻辑我们再来看看接下来的逻辑 image.png 可以看到父类创建了一个IOC容器和监听器之后,子类自己也创建了一个IOC容器并且放到了dispatcherServlet中。流程结束

tomcat启动过程中spring整合的过程

在onStartUp中创建了两个IOC容器,然后还有一个监听器就结束了,所以我们现在来看监听器的流程,他是如何把两个IOC容器整合并且执行refresh方法的

image.png 这是Spring创建的listener类图

image.png

在tomcat生命周期过程中会执行listener的方法,调用的是父类ContextLoader的init方法 image.png 随后tomcat初始化完成,执行dispatcherServlet的生命周期方法,把springIOC容器作为自己的父容器 image.png

SpringMVC想要获取父容器bean

AbstractBeanFactory中的getType方法中可以看到如果自己没有就找父类要的代码 image.png