1、DispatcherServlet作为统一访问点,进行全局的流程控制;
2、DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
3、DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
4、HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
5、ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
6、View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
7、返回控制权DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
在WEB容器如tomcat启动时会去读web.xml里的相关属性
加载完ContextLoaderListener后对
MVC的初始化是在servlet的init方法中进行的,其中initWebApplicationContext方法中configureAndRefreshWebApplicationContext方法是初始化ioc容器的(refresh方法所在),在这个之后进行了onRefresh方法调用。方法中初始化了mvc(有以下几个部分)。
注意:这里的获取是从之前ioc容器加载完成的基础上获取的(不用加载BeanDefinition)
dispatcherServlet的基类FrameworkServlet重写了HttpServlet的service以及doGet等方法其中调用了doService方法。对于include请求为request的attribute保存快照,然后分发http请求(doDispatch)。
doDispatch方法当中先检查请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析,再获得HandlerExecutionChain,获得第一个支持它的handlerAdapter(配置文件里配置的,如SimpleControllerHandlerAdapter实现是如果是Controller的子类就支持),进行http头部last-modified的处理(如果没修改直接返回不处理),interceptor预处理后adapter调用adapter的handler处理(适配后调用具体的handler的处理方法,可以自己实现adapter定义适配的内容)返回ModelAndView(如果没view使用默认view)后再interceptor后处理。如果ModelAndView中的view是名字引用则用viewResolver(也是配置文件里配置的)解析viewName成view(如果有现成的view则直接使用,常见的InternalResourceViewResolver就是用viewClass通过反射方式获得View实例(BeanUtils.instantiateClass)然后填充View类的url(pre+name+suf),attributeMap等属性),调用view的render ,将static attribute,pathVariables或model(也是attribute)以及requestContextAttribute合到一个map里,设置http一些头部信息,然后根据具体的view来进行具体的处理。如JSP的view是将map里的所有属性都加到request里(servletContext),再向request添加一些helper(FMT_TIME_ZONE,FMT_LOCALIZATION_CONTEXT等),获得view中之前设置的url封装成RequestDispatcher转发(forward)到内部定义好的资源上(如JSP页面,JSP页面的加载是web容器负责的,view只是起转发作用)。
获得HandlerExecutionChain:dispatcherServlet的handlerMapping属性的getHandler方法根据参数request返回HandlerExecutionChain(一个handler和一个interceptor列表),如URL的方式是从request中获得url,在handlerMap(url对应handler的map)中查找handler(也就是controller),如果找到就(如果获得的handler是beanName就getBean获得实例)进行验证后封装返回,否则遍历handlerMap的keyset也就是路径集合用matcher来匹配当前request的url,如有多个匹配就按默认排序后取第一个,同样处理返回。如果没有匹配的尝试root和default,如果成功同样验证封装返回。
这个handlerMap的注册是在查找handlerMappings时getBean中bean的postProcessor来完成的。在getBean中populateBean依赖注入之后的initializeBean中beanPostProcessor(ApplicationContextAwareProcessor)初始化ApplicationContext时调用registerHandlers注册handler。
注意:HandlerExecutionChain中实际上包装了handler和一个interceptor数组和list,这个数组好像一个最新加入的interceptor缓存一样,在chain中addInterceptor时要先把数组中的元素加到list中再把参数中的interceptor加到list中,getInterceptor获取interceptor时也是返回数组(如果数组里没有把list所有的都放在数组里再返回)。
View的种类,jstl就是jsp标签库,还有pdf,excel等实现。Jsp的实现之前介绍过了。
其中excel的实现是用POI (Apache POI)来实现的,POI的功能是提供API给Java程序对Microsoft Office格式档案读和写。Excel是用HSSFWorkbook作为excel的一个引用,如果指定的url是个excel就把这个excel加载进来(用resourceLoader封装成Resource然后读取其中的inputStream来创建新的HSSFWorkbook),否则新建一个空的HSSFWorkbook对象,其中buildExcelDocument方法让子类实现,提供具体的写入数据的方式。POI可以对sheet,row,col进行读取和写入。
Pdf的实现是用IText来实现的,IText是基于java的pdf文件生成类库。IText是用Document来作为Pdf的一个抽象,document可以add一个paragraph类增加文本。PdfWriter可以用来将一个现有的pdf文件加载到Document中,也可以设置pdf的一些属性等。