spring MVC
为什么在WEB下接入spring?
极高的提高开发效率
如何在web环境中整合spring容器?
常见的web容器是tomcat,在tomcat中主要的组件是servlet、监听器等,tomcat的配置在WEB-INF/web.xml中
常见配置:
1 | <?xml version="1.0" encoding="UTF-8"?> |
根容器的初始化
先来看看 ContextLoaderListener 有什么作用:
ContextLoaderListener 实现了 ServletContextListener 接口,ServletContextListener 是 tomcat 中的监听器:
1 | public interface ServletContextListener extends EventListener { |
接着看 ContextLoaderListener 如何实现 contextInitialized:
1 | class ContextLoaderListener { |
调用了父类方法 ContextLoader:
1 | class ContextLoader { |
接着看如何进行容器的实例化,进入 ContextLoader##createWebApplicationContext() :
1 | class ContextLoader { |
进入 ContextLoader##determineContextClass(),该方法决定容器的具体类型 :
1 | class ContextLoader { |
接着看如何配置容器并启动,进入 ContextLoader##configureAndRefreshWebApplicationContext():
1 | class ContextLoader { |
直此,根容器就启动起来,根容器 和 ServetContext 相互持有对方的实例,因此根容器所处的上下文就是 ServetContext 上下文,之后便可在web环境下使用spring容器的 IOC 和 AOP 功能。
spring MVC
MVC 就不必多言了,至于为什么选择 spring MVC,而不是struts2等,还不是因为它比较好用。
如何接入spring MVC 呢,还记得我们之前配置文件中配置的一个servlet吗?——DispatcherServlet
DispatcherServlet 作为 tomcat 的组件,有其web容器中的生命周期:
1 | public interface Servlet { |
所有 servlet 的init方法在 tomcat 启动时都会被调用,我们看 DispatcherServlet 的 init(),其实现在父类 HttpServletBean 中:
1 | class HttpServletBean { |
1
2
3
4
5
6 > PropertyValue 和 BeanWrapper 使用小案例:
> A a = new A();
> BeanWrapper beanWrapper = new BeanWrapperImpl(a);
> PropertyValue propertyValue = new PropertyValue("name","qqq");
> beanWrapper.setPropertyValue(propertyValue);
>
子类 FrameworkServlet 重写了 initServletBean 方法:
1 | class FrameworkServlet { |
进入 FrameworkServlet##initWebApplicationContext :
1 | class FrameworkServlet { |
为什么创建了两个容器?
根容器是web环境下使用的,管理tomcat中所有的bean,而子容器是servlet下使用的,仅管理springMVC中所有的Bean .
追溯 onRefresh(wac) ,可以走到 DispatcherServlet##initStrategies中
1 | class DispatcherServlet{ |
可以看到,initStrategies中做了大量的初始化工作,以 initHandlerMappings 为例说明:
1 | class DispatcherServlet{ |
至此,DispatcherServlet 中持有了另一个 spring容器。
那么来到第二个问题,servlet 如何工作?
当有请求到来时,根据web中定义的url映射会指派到指定的servlet中执行相应的service()方法, DispatcherServlet 的 service() 实现在 父类 FrameworkServlet 中
1 | class FrameworkServlet { |
进入 FrameworkServlet##processRequest
1 | class FrameworkServlet { |
进入 DispatcherServle##doService:
1 | class DispatcherServle{ |
进入 DispatcherServle##doDispatch:
1 | class DispatcherServle { |
要理解上面代码到底在做什么,我们得学习下面知识点:
- HandlerMapping:匹配url和处理器
- HandlerExecutionChain:包装处理器和拦截器
- HandlerAdapter:适配处理器:
- ModelAndView:模型和视图
- View:视图
HandlerMapping
1 | public interface HandlerMapping { |
AbstractHandlerMapping 做了基本的实现
1 | class AbstractHandlerMapping { |
getHandlerInternal 的匹配逻辑由子类实现,常用的有:
- AbstractHandlerMethodMapping:匹配url到一个Handler方法
- AbstractUrlHandlerMapping:匹配url到一个Handler类
以 AbstractUrlHandlerMapping 为例进行说明:
1 | abstract class AbstractUrlHandlerMapping { |
有子类去处理 handlerMap 的初始化,以 SimpleUrlHandlerMapping 为例:
1 | class SimpleUrlHandlerMapping { |
HandlerExecutionChain
HandlerExecutionChain 持有 处理器 和 拦截器
1 | public class HandlerExecutionChain { |
HandlerAdapter
1 | public interface HandlerAdapter { |
处理器的类型很多,统一适配成 HandlerAdapter 执行如:
- 接口 Controller,适配器 SimpleControllerHandlerAdapter
- 接口 HttpRequestHandler,适配器 HttpRequestHandlerAdapter
- 接口 Servlet,适配器 SimpleServletHandlerAdapter
以 SimpleControllerHandlerAdapter 为例:
1 | public class SimpleControllerHandlerAdapter implements HandlerAdapter { |
ModelAndView
持有 Model 和 View (或者 字符名)
1 | public class ModelAndView { |
View
1 | interface View { |
抽象实现:AbstractView
1 | abstract class AbstractView { |
具体如何解析返回,由子类实现 renderMergedOutputModel 来实现,如 pdf(AbstractPdfView),excel(AbstractXlsView),资源文件jsp(InternalResourceView)等等
1 | class AbstractPdfView { |
RequestToViewNameTranslator
1 | public interface RequestToViewNameTranslator { |
有一个默认实现 DefaultRequestToViewNameTranslator,列如:/www/qwe.jsp 将会解析为 www/qwe
ViewResolver
1 | public interface ViewResolver { |
如 InternalResourceViewResolver 将view字符名 解析为 InternalResourceView
DispatcherServle##doDispatch
再回到之前的 DispatcherServle##doDispatch 中:
1 | class DispatcherServle { |