chapter32_SpringMVC与DispatcherServlet

发布于:2025-04-20 ⋅ 阅读:(64) ⋅ 点赞:(0)

一、简介

从本章节开始进入SpringMVC的学习,SpringMVC最重要的类就是DispatcherServlet

DispatcherServlet的本质是一个Servlet,回顾一下Servlet

  • JavaWeb就是基于Servlet的
  • Servlet接口有5个方法
  • Servlet实现类是HttpServlet,自定义的Servlet需要继承HttpServlet,重写service方法
  • 使用SpringMVC后,DispatcherServlet就是唯一的Servlet,所有的请求由他分发

二、目标

  1. 手写SpringMVC的核心类DispatcherServlet
  2. 通过SCI与Tomcat对接

三、手写DispatcherServlet

新建抽象类FrameworkServlet,继承HttpServlet,它的功能是维护WebApplicationContext容器

  • 父容器在Spring对接SCI的时候刷新
  • 子容器在在DispatcherServlet的init方法刷新
/**
 * Spring 对 Servlet 的抽象实现类,负责管理 Web ioc 容器
 *
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/4/15 3:24
 * @Version 1.0
 */
public abstract class FrameworkServlet extends HttpServlet {

    // 子容器
    private ApplicationContext webApplicationContext;

    public FrameworkServlet(ApplicationContext webApplicationContext) {
        this.webApplicationContext = webApplicationContext;
    }

    @Override
    public void init() {
        initServletContext();
    }

    private void initServletContext() {
        ApplicationContext rootContext = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_NAME);
        AbstractRefreshableWebApplicationContext cwc = null;
        // 在springboot场景下会根据当前存在类创建不同ioc,在boot下直接不管
        if (this.webApplicationContext != null) {

            if (!(this.webApplicationContext instanceof AnnotationConfigApplicationContext)) {
                cwc = (AbstractRefreshableWebApplicationContext) this.webApplicationContext;
                if (cwc.getParent() == null) {
                    cwc.setParent(rootContext);
                }
                if (!cwc.isActive()) {
                    cwc.refresh();
                }
                cwc.setServletConfig(getServletConfig());
                cwc.setServletContext(getServletContext());
            }

            onRefresh(webApplicationContext);
        }
    }

    protected abstract void onRefresh(ApplicationContext applicationContext);
}

新建DispatcherServlet

  • 作为前置处理器
  • 实现service方法
public class DispatcherServlet extends FrameworkServlet {

    public DispatcherServlet(WebApplicationContext webApplicationContext) {
        super(webApplicationContext);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DispatcherServlet的service方法调用");
    }

    // 组件初始化,Servlet的init方法调用
    @Override
    protected void onRefresh(ApplicationContext applicationContext) {

    }
}

四、通过SCI与Tomcat对接

对接SCI,需要新建一个接口WebApplicationInitializer,所有实现了这个接口的类,容器启动的时候会自动调用其onStartup方法

public interface WebApplicationInitializer {

    /**
     * 所有实现了这个接口的类,容器启动的时候会自动调用其 onStartup 方法
     * @param servletContext Tomcat会传入servletContext
     */
    void onStartup(ServletContext servletContext);
}

新建一个抽象类AbstractDispatcherServletInitializer,实现WebApplicationInitializer接口,实现onStartup方法

/**
 * 对接SCI,实现onStartup方法
 *
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/4/15 3:29
 * @Version 1.0
 */
public abstract class AbstractDispatcherServletInitializer implements WebApplicationInitializer {

    public static final String DEFAULT_SERVLET_NAME = "dispatcher";

    public static final String DEFAULT_FILTER_NAME = "filters";

    public static final int M = 1024 * 1024;

    @Override
    public void onStartup(ServletContext servletContext) {
        // 创建父容器
        final AbstractApplicationContext rootApplicationContext = createRootApplicationContext();
        // 父容器放入servletContext
        servletContext.setAttribute(WebApplicationContext.ROOT_NAME, rootApplicationContext);
        // 刷新父容器(通过register配置类,所以需要手动刷新) -> 在源码中是通过事件进行refresh
        rootApplicationContext.refresh();

        final WebApplicationContext webApplicationContext = createWebApplicationContext();
        // 创建DispatcherServlet
        final DispatcherServlet dispatcherServlet = new DispatcherServlet(webApplicationContext);
        ServletRegistration.Dynamic dynamic = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);
        // 配置文件信息
        dynamic.setLoadOnStartup(1);
        final MultipartConfigElement configElement = new MultipartConfigElement(null, 5 * M, 5 * M, 5);
        dynamic.setMultipartConfig(configElement);
        dynamic.addMapping(getMappings());
        final Filter[] filters = getFilters();
        if (!ObjectUtil.isEmpty(filters)) {
            for (Filter filter : filters) {
                servletContext.addFilter(DEFAULT_FILTER_NAME, filter);
            }
        }

    }

    // 过滤器
    protected abstract Filter[] getFilters();

    // 映射器
    protected String[] getMappings() {
        return new String[]{"/"};
    }

    // 创建父容器,管理Service,Dao对象
    protected abstract AbstractApplicationContext createRootApplicationContext();

    // 创建子容器,管理Controller对象
    protected abstract WebApplicationContext createWebApplicationContext();

}

建抽象类AbstractAnnotationConfigDispatcherServletInitializer,继承AbstractDispatcherServletInitializer,实现创建父子容器的方法

  • 用户需要实现这个类,提供配置类
/**
 * 对接SCI,实现创建父子容器的方法
 *
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/4/15 4:01
 * @Version 1.0
 */
public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer {

    @Override
    protected AbstractApplicationContext createRootApplicationContext() {
        final Class<?> rootConfigClass = getRootConfigClass();
        if (ObjectUtil.isNotNull(rootConfigClass)) {
            final AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();
            rootContext.register(rootConfigClass);
            return rootContext;
        }
        return null;
     }

    @Override
    protected WebApplicationContext createWebApplicationContext() {
        final Class<?> webConfigClass = getWebConfigClass();
        if (ObjectUtil.isNotNull(webConfigClass)) {
            final AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
            webContext.register(webConfigClass);
            return webContext;
        }
        return null;
    }

		// 下面两个方法,由用户实现
    protected abstract Class<?> getRootConfigClass();

    protected abstract Class<?> getWebConfigClass();
}

新建SpringServletContainerInitializer类, 它负责spring与SCI的对接

  • 它的onStartup方法由Tomcat调用
  • 最终调用用户实现的WebApplicationInitializer类的onStartup
/**
 * Spring与SCI对接的类
 * 1.需要实现ServletContainerInitializer
 * 2.扫描 @HandlesTypes 指定的类
 *
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/4/15 4:13
 * @Version 1.0
 */
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    // 此方法由Tomcat调用
    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        if (webAppInitializerClasses.size() != 0) {
            final List<WebApplicationInitializer> initializers = new ArrayList<>(webAppInitializerClasses.size());
            // 排除接口和抽象类
            for (Class<?> webAppInitializerClass : webAppInitializerClasses) {
                if (!webAppInitializerClass.isInterface() && !Modifier.isAbstract(webAppInitializerClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(webAppInitializerClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)
                                ReflectUtil.getConstructor(webAppInitializerClass).newInstance());
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            }
            for (WebApplicationInitializer initializer : initializers) {
                initializer.onStartup(servletContext);
            }
        }
    }
}

配置SPI
请添加图片描述

请添加图片描述

五、测试

install一下chapter32模块

请添加图片描述

在tomcat9源码中导入pom依赖

<dependency>
    <groupId>cn.shopifymall</groupId>
    <artifactId>splendid-spring-chapter-32</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

提供配置类

/**
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/4/15 5:16
 * @Version 1.0
 */
@Configuration
@ComponentScan("cn.shopifymall.tomcat")
public class AppConfig {
}

提供用户类

/**
 * 此类为用户类,实现了 WebApplicationInitializer
 *
 * @Author 孤风雪影
 * @Email gitee.com/efairy520
 * @Date 2025/4/15 5:17
 * @Version 1.0
 */
public class QuickStart extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Filter[] getFilters() {
        return new Filter[0];
    }

    @Override
    protected Class<?> getRootConfigClass() {
        return AppConfig.class;
    }

    @Override
    protected Class<?> getWebConfigClass() {
        return AppConfig.class;
    }
}

运行Tomcat9的Bootstrap里面的main方法

  • 首先启动Tomcat
  • 识别到Pom依赖里面通过SPI注册的SpringServletContainerInitializer类,发现它是一个SCI
  • 扫描到所有的WebApplicationInitializer类型的用户类
  • 调用里面的onStartup方法,根据用户类提供的配置类,创建父子容器,并初始化DispatcherServlet
  • Tomcat启动完成,开始接收用户请求

请添加图片描述

启动后,访问任意接口

  • http://localhost:18080/
四月 16, 2025 3:02:26 上午 org.apache.catalina.startup.Catalina start
DispatcherServlet的service方法调用