内容简介:上一篇中通过前端控制器实现了接收请求. 控制器在收到请求后进行业务逻辑处理, 需要将视图返回至前端控制器, 由前端控制器并将结果返回至客户端.常见的返回结果有以下两种:前端控制器中需要接收控制器的返回结果, 在返回结果中约定: 如果以"redirect:"开始, 则为返回结果是重定向, 反之为视图, 前端控制器渲染视图后返回客户端.
上一篇中通过前端控制器实现了接收请求. 控制器在收到请求后进行业务逻辑处理, 需要将视图返回至前端控制器, 由前端控制器并将结果返回至客户端.
常见的返回结果有以下两种:
- HTML或其他数据格式
- 重定向
前端控制器中需要接收控制器的返回结果, 在返回结果中约定: 如果以"redirect:"开始, 则为返回结果是重定向, 反之为视图, 前端控制器渲染视图后返回客户端.
private void doService(HttpServletRequest req, HttpServletResponse resp) throws Exception { // 获取对应的方法 // 参数绑定 // ... // 执行控制器方法并接收返回值 String view = (String) method.invoke(classInstance, methodParams); // 根据返回值判断重定向或渲染视图(JSP) if (view.startsWith("redirect:")) { resp.sendRedirect(view.substring(9)); } else { // 将控制器中保持的变量设置到Request中 for (Map.Entry<String, Object> entry : model.entrySet()) { req.setAttribute(entry.getKey(), entry.getValue()); } // 跳转至相应的JSP中 req.getRequestDispatcher(view + ".jsp").forward(req, resp); } } 复制代码
支持多视图
随着模板的兴起, 现在有越来越多的模板技术取代JSP成为视图. 作为负责渲染视图的前端控制器也应该支持多种视图方式供用户选择.
配置视图类型
在前端控制器中定义视图类型, 并在初始化时加载.
// 视图类型(jsp,freemarker,velocity...) private String viewType = "jsp"; /** * 初始化Servlet. 容器初始化Servlet时调用, 加载配置文件初始化MVC相关组件(控制器,视图解析器等) */ @Override public void init() throws ServletException { // 获取用户自定义的视图类型 String viewTypeConfig = getInitParameter("viewType"); if (viewTypeConfig != null) { this.viewType = viewTypeConfig; } } 复制代码
在 web.xml
中配置视图类型
<servlet> <servlet-name>mvc</servlet-name> <servlet-class>com.atd681.xc.ssm.framework.DispatcherServlet</servlet-class> <!-- 视图类型 --> <init-param> <param-name>viewType</param-name> <param-value>freemarker</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 复制代码
根据视图类型渲染视图
在解析视图时, 根据配置的视图类型使用对应的方式解析视图
private void doService(HttpServletRequest req, HttpServletResponse resp) throws Exception { // 返回的视图 String view = (String) method.invoke(classInstance, methodParams); // 根据返回值判断重定向或渲染视图 if (view.startsWith("redirect:")) { resp.sendRedirect(view.substring(9)); } // 根据配置的视图类型使用对应的方式渲染视图 else { // 使用JSP作为视图 if ("jsp".equals(this.viewType)) { // 将控制器中保持的变量设置到Request中 for (Map.Entry<String, Object> entry : model.entrySet()) { req.setAttribute(entry.getKey(), entry.getValue()); } // 跳转至相应的JSP中 req.getRequestDispatcher(view + ".jsp").forward(req, resp); } // 使用Freemarker作为视图 else if ("freemarker".equals(this.viewType)) { Template template = new Configuration(Configuration.VERSION_2_3_0).getTemplate(view + ".ftl"); template.process(model, resp.getWriter()); } // 使用Velocity作为视图 else if ("velocity".equals(this.viewType)) { } // 其他各种视图... } } 复制代码
策略模式
上述方式是将所有视图解析的操作都在前端控制器中实现. 缺点显而易见:
- 随着所支持视图的增加, 前端控制器中的视图渲染部分需要不断的增加分支.
- 某个类型的视图需要修改或升级时, 需要在前端控制器代码进行修改
- 前端控制器代码量巨大, 越来越难维护.
设计模式中有一种模式叫做策略模式: 将算法独立封装, 使得算法可以自由切换. 换成视图的方式就是: 将每种类型视图的渲染方式封装成独立的策略类(对外提供渲染视图的方法). 前端控制器根据配置找到对应的视图策略类, 调用渲染视图方法.
定义视图策略统一接口
// 策略接口 public interface ViewResolver { // 渲染视图方法 void render(HttpServletRequest req, HttpServletResponse resp, String viewName, Map<String, Object> model) throws Exception; } 复制代码
实现各个视图策略类
JSP
// JSP视图解析策略 public class JSPViewResolver implements ViewResolver { // 使用JSP渲染视图 @Override public void render(HttpServletRequest req, HttpServletResponse resp, String viewName, Map<String, Object> model) throws Exception { // 将控制器中保持的变量设置到Request中 for (Map.Entry<String, Object> entry : model.entrySet()) { req.setAttribute(entry.getKey(), entry.getValue()); } // 跳转至相应的JSP中 req.getRequestDispatcher(viewName + ".jsp").forward(req, resp); } } 复制代码
Freemarker
// Freemarker视图解析策略 public class FreemarkerViewResolver implements ViewResolver { // Freemarker配置 private Configuration config = new Configuration(Configuration.VERSION_2_3_0); // 使用Freemarker渲染视图 @Override public void render(HttpServletRequest req, HttpServletResponse resp, String viewName, Map<String, Object> model) throws Exception { Template template = config.getTemplate(viewName + ".ftl"); template.process(model, resp.getWriter()); } } 复制代码
前端控制器找到对应的策略
前端控制器如何能够根据配置的视图类型找到对应的策略类并且实例化呢?
在 web.xml
中配置视图类型时使用对应的视图策略类的名称, 前端控制器通过 JAVA 发射就可以实例化策略类并调用其渲染视图的方法.
在 web.xml
中配置视图策略类
<servlet> <servlet-name>mvc</servlet-name> <servlet-class>com.atd681.xc.ssm.framework.DispatcherServlet</servlet-class> <!-- 视图类型 --> <init-param> <param-name>viewClass</param-name> <param-value>com.atd681.xc.ssm.framework.view.FreemarkerViewResolver</param-value> </init-param> </servlet> 复制代码
在前端控制器中定义视图类型, 并在初始化时加载.
// 视图策略类路径(jsp,freemarker,velocity...) private String viewClass = "com.atd681.xc.ssm.framework.view.JSPViewResolver"; /** * 初始化Servlet. 容器初始化Servlet时调用, 加载配置文件初始化MVC相关组件(控制器,视图解析器等) */ @Override public void init() throws ServletException { // 获取用户自定义的视图策略类路径 String viewClassConfig = getInitParameter("viewClass"); if (viewClassConfig != null) { this.viewClass = viewClassConfig; } } 复制代码
实例化视图策略类并渲染视图
在解析视图时, 根据配置的视图类型使用对应的方式解析视图
private void doService(HttpServletRequest req, HttpServletResponse resp) throws Exception { // 返回的视图 String view = (String) method.invoke(classInstance, methodParams); // 根据返回值判断重定向或渲染视图 if (view.startsWith("redirect:")) { resp.sendRedirect(view.substring(9)); } // 根据配置的视图类型使用对应的方式渲染视图 else { // 实例化对应的策略类 ViewResolver viewResolver = (ViewResolver) Class.forName(this.viewClass).newInstance(); // 使用视图策略类封装的渲染方法 viewResolver.render(req, resp, view, model); } } 复制代码
后续章节讲到Spring IOC容器时, 实例化视图策略可以放到IOC容器.
视图解析使用策略模式后, 大大简化了代码的复杂度. 当需要增加视图或修改视图时只需要增加或修改相应的视图策略类即可. 对前端控制器的逻辑没有任何影响.
总结
策略模式是一种很常见的设计模式, 结合Spring IOC使用非常的简单便捷. 使用策略模式可以大大降低代码的复杂度, Spring IOC下的策略模式天生就是为了解决过多的 if/else
SpringMVC的视图解析为了支持多视图解析, 增加了View的概念, View负责解析视图. ViewResolver负责获取对应的视图. 但整体思路 有兴趣的读者可以看下Spring的源码.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring Boot实践——SpringMVC视图解析
- SpringMVC源码分析6:SpringMVC的视图解析原理
- iOS小技巧·把子视图控制器的视图添加到父视图控制器
- CouchDB 视图简介及增量更新视图的方法
- c# – 将数据从部分视图传递到其父视图
- Django 基于函数的视图与基于类的视图
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Imperfect C++中文版
威尔逊 / 荣耀、刘未鹏 / 人民邮电出版社 / 2006-1 / 75.0
汇集实用的C++编程解决方案,C++虽然是一门非凡的语言,但并不完美。Matthew Wilson使用C++十年有余,其间发现C++存在一些固有的限制,需要一些颇具技术性的工作进行弥补。本书不仅指出了C++的缺失,更为你编写健壮、灵活、高效、可维护的代码提供了实用的技术和工具。Wilson向你展示了如何克服C++的复杂性,穿越C++庞大的范式阵列。夺回对代码的控制权,从而获得更理想的结果。一起来看看 《Imperfect C++中文版》 这本书的介绍吧!