SpringMVCalready

Spring MVC-注解开发-初始化过程

2022-05-15  本文已影响0人  石头耳东

前置文章:
Spring MVC-简单使用

零、本文纲要

一、快速入门
二、初始化过程

  1. Servlet规范
    1.1 补充:POST请求乱码处理
  2. 初始化过程分析

一、快速入门

1. 基础依赖

① spring-context
② spring-webmvc
③ javax.servlet-api

2. 配置类

① SpringConfig
② SpringMvcConfig
③ ServletContainersInitConfig

二、初始化过程

1. Servlet规范

ServletContainerInitializer 是 Servlet 3.0 新增的一个接口,主要用于在容器启动阶段通过编程风格注册Filter, Servlet以及Listener,以取代通过web.xml配置注册。
Servlet 3.0 规范中,容器启动阶段会读取项目的/META-INF/services/javax.servlet.ServletContainerInitializer文件,进行初始化准备工作。

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    ...
}

Ⅰ @HandlesTypes(WebApplicationInitializer.class)

作用:是将注解指定的Class对象作为参数传递到ServletContainerInitializer#onStartup方法中。

Ⅱ WebApplicationInitializer

该接口我们需要实现,以将实现类传递给ServletContainerInitializer#onStartup方法。

public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

所以我们MVC项目中需要编写该WebApplicationInitializer接口的实现类,来进行一些自定义的配置。

Ⅲ AbstractDispatcherServletInitializer

常常我们会继承该抽象类,其也是WebApplicationInitializer接口的抽象实现类。
DispatcherServlet是我们熟悉的Spring MVC项目中的前端控制器。

public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    //加载springmvc配置类
    protected WebApplicationContext createServletApplicationContext() {
        //初始化WebApplicationContext对象
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        //加载指定配置类
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }

    //设置由springmvc控制器处理的请求映射路径
    protected String[] getServletMappings() {
        return new String[]{"/"}; // 表示所有请求路径由springmvc控制器处理
    }

    //加载spring配置类
    protected WebApplicationContext createRootApplicationContext() {
        //初始化WebApplicationContext对象
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        //加载指定配置类
        ctx.register(SpringConfig.class);
        return ctx;
    }
}

注意:如果写成WebApplicationContext ctx = new AnnotationConfigWebApplicationContext();,则无法调用子类的register方法。

1.1 补充:POST请求乱码处理

//POST请求乱码处理
@Override
protected Filter[] getServletFilters() {
    CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding("UTF-8");
    return new Filter[]{filter};
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    //触发父类的onStartup
    super.onStartup(servletContext);
    //1.创建字符集过滤器对象
    CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
    //2.设置使用的字符集
    characterEncodingFilter.setEncoding("UTF-8");
    //3.添加到容器(它不是IOC容器,而是ServletContainer)
    FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter",characterEncodingFilter);
}

2. 初始化过程分析

for (WebApplicationInitializer initializer : initializers) {
    //【断点】
    initializer.onStartup(servletContext);
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    //【断点】此处会完成Spring配置类的注册
    super.onStartup(servletContext);
    //【断点】此处会往ServletContext中注册前端控制器DispatcherServlet
    registerDispatcherServlet(servletContext);
}

【重点】此处会完成RootApplicationContext父容器的初始化。

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    //【断点】
    registerContextLoaderListener(servletContext);
}
protected void registerContextLoaderListener(ServletContext servletContext) {
    //【断点】调用我们编写的ServletContainersInitConfig配置类的createRootApplicationContext方法
    WebApplicationContext rootAppContext = createRootApplicationContext();
    if (rootAppContext != null) {
        //把ContextLoaderListener添加到ServletContext中
        ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
        listener.setContextInitializers(getRootApplicationContextInitializers());
        servletContext.addListener(listener);
    }
    else {
        logger.debug("No ContextLoaderListener registered, as " +
                "createRootApplicationContext() did not return an application context");
    }
}

我们编写的ServletContainersInitConfig继承了AbstractDispatcherServletInitializer,而且AbstractDispatcherServletInitializer继承了AbstractContextLoaderInitializer。
所以,此处调用的createRootApplicationContext方法,实际上就是我们ServletContainersInitConfig配置类内部重写的方法。

【重点】此处会完成ServletApplicationContext子容器的初始化,并且添加映射规则,添加自定义过滤器。

protected void registerDispatcherServlet(ServletContext servletContext) {
    String servletName = getServletName();
    Assert.hasLength(servletName, "getServletName() must not return null or empty");
    //【断点】调用我们编写的ServletContainersInitConfig配置类的createServletApplicationContext方法
    WebApplicationContext servletAppContext = createServletApplicationContext();
    Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

    FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
    Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
    dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

    //往servletContext容器中添加前端控制器dispatcherServlet
    ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
    if (registration == null) {
        throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
                "Check if there is another servlet registered under the same name.");
    }

    //设置加载时机
    registration.setLoadOnStartup(1);
    //【断点】添加映射,调用我们编写的ServletContainersInitConfig配置类的getServletMappings方法
    registration.addMapping(getServletMappings());
    registration.setAsyncSupported(isAsyncSupported());

    //【断点】添加过滤器,调用我们编写的ServletContainersInitConfig配置类的getServletFilters方法;如果没有重写,则返回null
    Filter[] filters = getServletFilters();
    if (!ObjectUtils.isEmpty(filters)) {
        for (Filter filter : filters) {
            registerServletFilter(servletContext, filter);
        }
    }

    customizeRegistration(registration);
}

三、结尾

以上即为Spring MVC-注解开发-初始化过程的基础内容,感谢阅读。

上一篇 下一篇

猜你喜欢

热点阅读