SpringMVC

 

 

SpringMVC大致脉络图:

 

 

 

RequestProcessorChain:

  参照的是之前学习Spring的时候经常遇到的责任链模式的后置处理器。该类中保存了RequestProcessor的多个实现类,之所以会有多个不同的实现类是因为DispatcherServlet是项目中所有请求的唯一入口,这些请求中即会有获取JSP页面的请求也会有获取静态资源的请求,同时还会有直接获取json数据的请求等等。针对不同的资源请求会使用不同的RequestProcessor处理。在RequestProcessor矩阵里面会按照顺序依次去执行他们。

 

RequestProcessor矩阵:

  PreRequestProcessor:该处理器主要负责对请求的编码以及路径作一些前置性的处理。是一个必须要执行的处理器

  StaticResourceRequestProcessor:该处理器主要负责对静态资源请求,如图片、css、js等资源的请求进行处理

  JspRequestProcessor:该处理器主要负责对JSP页面的访问请求进行处理(如果不经过Contoller方法的转发直接访问某个jsp页面,相关的请求就是该类处理)

  ContollerRequestProcessor:该处理器主要负责对将请求派发到对应的Contoller方法里进行处理

 

Render矩阵:处理器链执行完之后呢(当请求处理成功后),就会调用特定的实现了ResultRender接口的实现类,对处理结果进行展现

  DefaultResultRender:如果用户只需要返回一个成功的状态码,则可以选择该Render进行渲染

  JsonResultRender:如果用户发生的请求是想要获取json格式的返回结果,则可以选择该Render进行渲染

  ViewResultRender:针对页面的渲染需求,则可以选择该Render进行渲染(类似modelAndView对象)

  InternalErrorResultRender:针对程序的异常,需要有专门的内部异常渲染器对结果进行处理,则可以选择该Render进行渲染

  ResourceNotFoundResultRender:针对请求资源无法找到的情况,则可以选择该Render进行渲染

 


 

SpringMVC的环境搭建以及初始化入口分析:

传统的SpringMVC采用xml文件配置:

 

 

 我们跟进到ContextLoaderListener类中,该类实现了ServletContextListener接口,所以本质上它是一个Servlet监听器。

Tomcat会优先加载Servlet监听器组件,以保证在Servket被创建之前即Context组件在进行初始化的时候去调用类中的contextInitialized()方法。

public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
}

该方法根据配置文件的contexConfigLocation配置去读取解析Spring容器的配置xml,去创建并刷新容器的实例。

contexConfigLocation(). initWebApplicationContext().configureAndRefreshWebApplicationContext()  :会去配置并刷新容器,该方法里面回去调用容器的refresh()方法去创建刷新容器

容器配置刷新完之后,会将ApplicationContext(容器)存放入(Tomcat的)ServletContext中,供后续Dispatcher使用  

 

 

SpringMVC采用注解方式配置:

注解形式的配置是在Servlet3.0后开始有。此时采用@WebServlet、@WebFilter等注解去取代web.xml的配置。

在Servlet3.0中还通过让用户去实现一些接口去动态注册Servlet,Filter,Listener。

跟进到3.0才有的ServletContainerInitializer接口,Tomcat等web容器在启动的时候会使用JAR API来发现ServletContainerInitializer的实现类并调用实现类的onStartup方法来处理。

可以跟进到SpringServletContainerInitializer类中,该类就是Spring自带的ServletContainerInitializer的实现类。可以看到它的onStartup()方法。方法中并没有Servlet被初始化的地方。

方法中可以看到有去for循环调用WebApplicationInitializer接口实现类的onStartup,ServletContext和Servlet就是在这个地方被创建出来的。

for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
}

 

WebApplicationInitializer接口实现类是通过进行注入的。该标签就类似Spring中的@Autowired会自带找到标签属性的class实现类并注入到容器来

 

 

该接口的实现类有三个:

 

 

 通过名字可以看到和Servlet相关的是AbstractDispatcherServletInitializer。进入到AbstractDispatcherServletInitializer类中

public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        registerDispatcherServlet(servletContext);

}

可以看到如果是以注解的方式来配置Servlet,DispatcherServlet就是在此处注册进来的。

registerDispatcherServlet():此处用到了模板方法设计模式

  (1)创建SpringMVC  ioc容器  (2)创建DispatcherServlet实例   (3)路径映射 (4)请求拦截器(可在此处控制统一编码)

 


 

 

WebApplicationInitializer的加载成员:

 

 

 我们只需要去继承实现AbstractAnnotationConfigDispatcherServletInitializer抽象类,即可实现通过注解的形式来配置SpringMVC

package com.imooc.config;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;

public class StartWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //springcontext中相关的bean
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringRootConfig.class};
//SpringContext中相关的bean
@Configuration//以让其作为配置容器的bean注册到容器中
@ComponentScan("com.imooc.service")//让这个root容器去扫描除了controller层以外的bean
public class SpringRootConfig {
}
  }
//DispatcherServlet中 上下文相关的bean
    @Override
    protected Class<?>[] getServletConfigClasses() {
    return new Class[]{MVCConfig.class};
@Configuration//以让其作为配置容器的bean注册到容器中
@ComponentScan("com.imooc.controller")//让这个root容器去扫描除了controller层以外的bean
@EnableWebMvc//开启容器的mvc功能
public class MVCConfig {
    @Bean//加了该注解,Spring容器就会在启动并读入MVCConfig这个配置类的时候去调用下面这个方法,将返回值作为bean存到容器中
    public InternalResourceViewResolver viewResolver(){//InteralResourceViewResolver : 内部资源视图解析器
        InternalResourceViewResolver internalResourceViewResolver  = new InternalResourceViewResolver();
        internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }
}
    }
   
    //Servlet请求映射路径
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};//表明所有的请求都会经由这个DispatcherServlet来处理
    }

    //拦截并处理请求的编码
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceEncoding(true);
        return new Filter[]{encodingFilter};
    }
}

 

 

 


 

 

SpringMVC的核心流程:

  建立请求和Controller方法的映射集合的流程

  根据请求查找对应的Controller方法的流程

  请求参数绑定到方法形参,执行方法处理请求,渲染视图的流程

 

 

 

 

 

 


 

前言:

  (1)注解的初始化(AbstractDispatcherServletInitializer.registerDispatcherServlet())会先于Web.xml的初始化(ContextLoaderListener.contextInitialized())去执行。

  (2)DispatcherServlet的关系图:

 

 

ContextLoaderListener.contextInitialized().initWebApplicationContext()

执行到这个方法的时候,相关的root容器、子容器、DispatcherServlet实例都已经创建好了。跟进到方法源码中:

  (1)在子容器(ServletContext)中查找root容器,前面只是创建了root容器,并没有关联起来,所以这里不会抛出异常

  (2)如果容器还没刷新过,则调用configureAndRefreshWebApplicationContext()方法去配置并刷新容器

      此时会先去调用AbstractApplicationContext的refresh()刷新容器(对非mvc的bean进行加载),而对于MVC的Bean加载是在子容器(ServletContext)的refresh中进行。

      在DispatcherServlet的关系图中我们可以看到它实现了HttpServlet。在Servlet的初始化时回去调用init()方法。而子容器(ServletContext)的刷新(refresh)就

      是在DispatcherServlet初始化的时候调用init()方法调用的。此时DispatcherServlet的回去调用父类FrameworkServlet里的configureAndRefreshWebApplicationContext()方法对容器进行刷新

 

----以上总结:

  Root容器的刷新是在ContextLoaderListener.contextInitialized().initWebApplicationContext().configureAndRefreshWebApplicationContext().refresh()方法中

  子容器(ServletContext)的刷新是在DispatcherServlet初始化调用到父类FrameworkServlet里的configureAndRefreshWebApplicationContext().refresh()方法中

 

 


 

 

 

posted @ 2021-03-04 22:24  _kerry  阅读(92)  评论(0编辑  收藏  举报