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的核心流程:
前言:
(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()方法中